2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Kernel
|
2010-01-10 15:40:00 +00:00
|
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
2006-11-08 11:47:44 +00:00
|
|
|
* FILE: ntoskrnl/ke/i386/v86vdm.c
|
2010-01-10 15:40:00 +00:00
|
|
|
* PURPOSE: V8086 and VDM Trap Emulation
|
|
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
|
|
* Alex Ionescu (alex.ionescu@reactos.org)
|
2006-11-08 11:47:44 +00:00
|
|
|
*/
|
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* INCLUDES *******************************************************************/
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2010-01-13 03:43:03 +00:00
|
|
|
#define KiVdmGetInstructionSize(x) ((x) & 0xFF)
|
|
|
|
#define KiVdmGetPrefixFlags(x) ((x) & 0xFFFFFF00)
|
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* GLOBALS ********************************************************************/
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
ULONG KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE;
|
|
|
|
ULONG KeI386EFlagsOrMaskV86 = EFLAGS_INTERRUPT_MASK;
|
|
|
|
PVOID Ki386IopmSaveArea;
|
|
|
|
BOOLEAN KeI386VirtualIntExtensions = FALSE;
|
2010-01-10 15:40:00 +00:00
|
|
|
const PULONG KiNtVdmState = (PULONG)FIXED_NTVDMSTATE_LINEAR_PC_AT;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* UNHANDLED OPCODES **********************************************************/
|
|
|
|
|
|
|
|
KiVdmUnhandledOpcode(F);
|
|
|
|
KiVdmUnhandledOpcode(OUTSW);
|
|
|
|
KiVdmUnhandledOpcode(OUTSB);
|
|
|
|
KiVdmUnhandledOpcode(INSB);
|
|
|
|
KiVdmUnhandledOpcode(INSW);
|
|
|
|
KiVdmUnhandledOpcode(NPX);
|
|
|
|
KiVdmUnhandledOpcode(INBimm);
|
|
|
|
KiVdmUnhandledOpcode(INWimm);
|
|
|
|
KiVdmUnhandledOpcode(OUTBimm);
|
|
|
|
KiVdmUnhandledOpcode(OUTWimm);
|
|
|
|
KiVdmUnhandledOpcode(INB);
|
|
|
|
KiVdmUnhandledOpcode(INW);
|
|
|
|
KiVdmUnhandledOpcode(OUTB);
|
|
|
|
KiVdmUnhandledOpcode(OUTW);
|
|
|
|
KiVdmUnhandledOpcode(HLT);
|
|
|
|
KiVdmUnhandledOpcode(INTO);
|
|
|
|
KiVdmUnhandledOpcode(INV);
|
|
|
|
|
|
|
|
/* OPCODE HANDLERS ************************************************************/
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodePUSHF(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
|
|
|
ULONG Esp, V86EFlags, TrapEFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Get current V8086 flags and mask out interrupt flag */
|
|
|
|
V86EFlags = *KiNtVdmState;
|
|
|
|
V86EFlags &= ~EFLAGS_INTERRUPT_MASK;
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
|
2013-04-25 21:29:59 +00:00
|
|
|
/* Get trap frame EFLags */
|
2010-01-10 15:40:00 +00:00
|
|
|
TrapEFlags = TrapFrame->EFlags;
|
2013-04-25 21:29:59 +00:00
|
|
|
/* Check for VME support */
|
|
|
|
if(KeI386VirtualIntExtensions)
|
|
|
|
{
|
|
|
|
/* Copy the virtual interrupt flag to the interrupt flag */
|
|
|
|
TrapEFlags &= ~EFLAGS_INTERRUPT_MASK;
|
|
|
|
if(TrapEFlags & EFLAGS_VIF)
|
|
|
|
TrapEFlags |= EFLAGS_INTERRUPT_MASK;
|
|
|
|
}
|
|
|
|
/* Leave only align, nested task and interrupt */
|
|
|
|
TrapEFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Add in those flags if they exist, and add in the IOPL flag */
|
|
|
|
V86EFlags |= TrapEFlags;
|
|
|
|
V86EFlags |= EFLAGS_IOPL;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Build flat ESP */
|
|
|
|
Esp = (TrapFrame->HardwareSegSs << 4) + (USHORT)TrapFrame->HardwareEsp;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Check for OPER32 */
|
2010-01-13 03:43:03 +00:00
|
|
|
if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
|
2010-01-10 15:40:00 +00:00
|
|
|
{
|
|
|
|
/* Save EFlags */
|
2010-09-11 09:20:26 +00:00
|
|
|
Esp -= 4;
|
2010-09-16 19:21:20 +00:00
|
|
|
*(PULONG)Esp = V86EFlags;
|
2010-01-10 15:40:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Save EFLags */
|
2010-09-16 19:21:20 +00:00
|
|
|
Esp -= 2;
|
2010-01-10 15:40:00 +00:00
|
|
|
*(PUSHORT)Esp = (USHORT)V86EFlags;
|
|
|
|
}
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Set new ESP and EIP */
|
2010-09-16 19:21:20 +00:00
|
|
|
TrapFrame->HardwareEsp = Esp - (TrapFrame->HardwareSegSs << 4);
|
2010-01-13 03:43:03 +00:00
|
|
|
TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* We're done */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodePOPF(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
|
|
|
ULONG Esp, V86EFlags, EFlags, TrapEFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Build flat ESP */
|
|
|
|
Esp = (TrapFrame->HardwareSegSs << 4) + (USHORT)TrapFrame->HardwareEsp;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Check for OPER32 */
|
2010-09-11 09:20:26 +00:00
|
|
|
if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
|
2010-01-10 15:40:00 +00:00
|
|
|
{
|
2010-09-11 09:20:26 +00:00
|
|
|
/* Read EFlags */
|
|
|
|
EFlags = *(PULONG)Esp;
|
|
|
|
Esp += 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Read EFlags */
|
|
|
|
EFlags = *(PUSHORT)Esp;
|
|
|
|
Esp += 2;
|
2010-01-10 15:40:00 +00:00
|
|
|
}
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Set new ESP */
|
2010-09-16 19:21:20 +00:00
|
|
|
TrapFrame->HardwareEsp = Esp - (TrapFrame->HardwareSegSs << 4);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Mask out IOPL from the flags */
|
|
|
|
EFlags &= ~EFLAGS_IOPL;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Save the V86 flags, but mask out the nested task flag */
|
|
|
|
V86EFlags = EFlags & ~EFLAGS_NESTED_TASK;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Now leave only alignment, nested task and interrupt flag */
|
|
|
|
EFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
/* Get trap EFlags */
|
|
|
|
TrapEFlags = TrapFrame->EFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
/* Check for VME support */
|
2013-04-25 21:29:59 +00:00
|
|
|
if(KeI386VirtualIntExtensions)
|
|
|
|
{
|
|
|
|
/* Copy the IF flag into the VIF one */
|
|
|
|
V86EFlags &= ~EFLAGS_VIF;
|
|
|
|
if(V86EFlags & EFLAGS_INTERRUPT_MASK)
|
|
|
|
{
|
|
|
|
V86EFlags |= EFLAGS_VIF;
|
|
|
|
/* Don't set the interrupt flag */
|
|
|
|
V86EFlags &= ~EFLAGS_INTERRUPT_MASK;
|
|
|
|
}
|
|
|
|
}
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
|
2013-04-25 21:29:59 +00:00
|
|
|
/* Add V86 flag */
|
|
|
|
V86EFlags |= EFLAGS_V86_MASK;
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Update EFlags in trap frame */
|
2013-04-29 16:17:46 +00:00
|
|
|
TrapFrame->EFlags |= V86EFlags;
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Check if ESP0 needs to be fixed up */
|
|
|
|
if (TrapEFlags & EFLAGS_V86_MASK) Ki386AdjustEsp0(TrapFrame);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Update the V8086 EFlags state */
|
|
|
|
KiVdmClearVdmEFlags(EFLAGS_ALIGN_CHECK | EFLAGS_NESTED_TASK | EFLAGS_INTERRUPT_MASK);
|
|
|
|
KiVdmSetVdmEFlags(EFlags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* FIXME: Check for VDM interrupts */
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Update EIP */
|
2010-01-13 03:43:03 +00:00
|
|
|
TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* We're done */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodeINTnn(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
|
|
|
ULONG Esp, V86EFlags, TrapEFlags, Eip, Interrupt;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Read trap frame EFlags */
|
|
|
|
TrapEFlags = TrapFrame->EFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Remove interrupt flag from V8086 EFlags */
|
|
|
|
V86EFlags = *KiNtVdmState;
|
|
|
|
KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Keep only alignment and interrupt flag from the V8086 state */
|
|
|
|
V86EFlags &= (EFLAGS_ALIGN_CHECK | EFLAGS_INTERRUPT_MASK);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
/* Check for VME support */
|
|
|
|
ASSERT(KeI386VirtualIntExtensions == FALSE);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Mask in the relevant V86 EFlags into the trap flags */
|
|
|
|
V86EFlags |= (TrapEFlags & ~EFLAGS_INTERRUPT_MASK);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* And mask out the VIF, nested task and TF flag from the trap flags */
|
|
|
|
TrapFrame->EFlags = TrapEFlags &~ (EFLAGS_VIF | EFLAGS_NESTED_TASK | EFLAGS_TF);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Add the IOPL flag to the local trap flags */
|
|
|
|
V86EFlags |= EFLAGS_IOPL;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Build flat ESP */
|
|
|
|
Esp = (TrapFrame->HardwareSegSs << 4) + TrapFrame->HardwareEsp;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Push EFlags */
|
|
|
|
Esp -= 2;
|
|
|
|
*(PUSHORT)(Esp) = (USHORT)V86EFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Push CS */
|
|
|
|
Esp -= 2;
|
|
|
|
*(PUSHORT)(Esp) = (USHORT)TrapFrame->SegCs;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Push IP */
|
|
|
|
Esp -= 2;
|
2010-01-13 03:43:03 +00:00
|
|
|
*(PUSHORT)(Esp) = (USHORT)TrapFrame->Eip + KiVdmGetInstructionSize(Flags) + 1;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Update ESP */
|
|
|
|
TrapFrame->HardwareEsp = (USHORT)Esp;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Get flat EIP */
|
|
|
|
Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Now get the *next* EIP address (current is original + the count - 1) */
|
2010-01-13 03:43:03 +00:00
|
|
|
Eip += KiVdmGetInstructionSize(Flags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Now read the interrupt number */
|
|
|
|
Interrupt = *(PUCHAR)Eip;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Read the EIP from its IVT entry */
|
|
|
|
Interrupt = *(PULONG)(Interrupt * 4);
|
|
|
|
TrapFrame->Eip = (USHORT)Interrupt;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Now get the CS segment */
|
|
|
|
Interrupt = (USHORT)(Interrupt >> 16);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Check if the trap was not V8086 trap */
|
|
|
|
if (!(TrapFrame->EFlags & EFLAGS_V86_MASK))
|
|
|
|
{
|
|
|
|
/* Was it a kernel CS? */
|
|
|
|
Interrupt |= RPL_MASK;
|
|
|
|
if (TrapFrame->SegCs == KGDT_R0_CODE)
|
|
|
|
{
|
|
|
|
/* Add the RPL mask */
|
|
|
|
TrapFrame->SegCs = Interrupt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set user CS */
|
|
|
|
TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set IVT CS */
|
|
|
|
TrapFrame->SegCs = Interrupt;
|
|
|
|
}
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* We're done */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodeIRET(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
|
|
|
ULONG Esp, V86EFlags, EFlags, TrapEFlags, Eip;
|
2010-01-13 03:43:03 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Build flat ESP */
|
|
|
|
Esp = (TrapFrame->HardwareSegSs << 4) + TrapFrame->HardwareEsp;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Check for OPER32 */
|
2010-01-13 03:43:03 +00:00
|
|
|
if (KiVdmGetPrefixFlags(Flags) & PFX_FLAG_OPER32)
|
2010-01-10 15:40:00 +00:00
|
|
|
{
|
|
|
|
/* Build segmented EIP */
|
|
|
|
TrapFrame->Eip = *(PULONG)Esp;
|
|
|
|
TrapFrame->SegCs = *(PUSHORT)(Esp + 4);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Set new ESP */
|
|
|
|
TrapFrame->HardwareEsp += 12;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Get EFLAGS */
|
|
|
|
EFlags = *(PULONG)(Esp + 8);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Build segmented EIP */
|
|
|
|
TrapFrame->Eip = *(PUSHORT)Esp;
|
|
|
|
TrapFrame->SegCs = *(PUSHORT)(Esp + 2);
|
|
|
|
|
|
|
|
/* Set new ESP */
|
|
|
|
TrapFrame->HardwareEsp += 6;
|
|
|
|
|
|
|
|
/* Get EFLAGS */
|
|
|
|
EFlags = *(PUSHORT)(Esp + 4);
|
|
|
|
}
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Mask out EFlags */
|
|
|
|
EFlags &= ~(EFLAGS_IOPL + EFLAGS_VIF + EFLAGS_NESTED_TASK + EFLAGS_VIP);
|
|
|
|
V86EFlags = EFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
/* Check for VME support */
|
|
|
|
ASSERT(KeI386VirtualIntExtensions == FALSE);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Add V86 and Interrupt flag */
|
|
|
|
EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Update EFlags in trap frame */
|
|
|
|
TrapEFlags = TrapFrame->EFlags;
|
|
|
|
TrapFrame->EFlags = (TrapFrame->EFlags & EFLAGS_VIP) | EFlags;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Check if ESP0 needs to be fixed up */
|
|
|
|
if (!(TrapEFlags & EFLAGS_V86_MASK)) Ki386AdjustEsp0(TrapFrame);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Update the V8086 EFlags state */
|
|
|
|
KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
|
|
|
|
KiVdmSetVdmEFlags(V86EFlags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Build flat EIP and check if this is the BOP instruction */
|
|
|
|
Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
|
|
|
|
if (*(PUSHORT)Eip == 0xC4C4)
|
|
|
|
{
|
|
|
|
/* Dispatch the BOP */
|
|
|
|
VdmDispatchBop(TrapFrame);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* FIXME: Check for VDM interrupts */
|
2010-09-11 09:20:26 +00:00
|
|
|
DPRINT("FIXME: Check for VDM interrupts\n");
|
2010-01-10 15:40:00 +00:00
|
|
|
}
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* We're done */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodeCLI(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
2013-03-10 11:44:04 +00:00
|
|
|
{
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
/* Check for VME support */
|
|
|
|
ASSERT(KeI386VirtualIntExtensions == FALSE);
|
2010-01-10 15:40:00 +00:00
|
|
|
|
2010-01-13 03:43:03 +00:00
|
|
|
/* Disable interrupts */
|
2010-01-10 15:40:00 +00:00
|
|
|
KiVdmClearVdmEFlags(EFLAGS_INTERRUPT_MASK);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Skip instruction */
|
2010-01-13 03:43:03 +00:00
|
|
|
TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Done */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodeSTI(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
[NTOS/PERF]: Enable VME support. VME stands for Virtual 8086 Mode Extensions, and it's an Intel optimization that makes changes to the IF bit in EFLAGS (CLI, STI, INT, IRETD, PUSHF, POPF) completely transprent: instead of changing the real (protected) bit, which requires the OS to trap and emulate the behavior, the CPU sets a "Fake" IF bit instead. When you're dong in V8086 mode, you simply update your real flag with whatever the fake flag says.
[NTOS]: Enable V8086 Fast-V86 Trap mode for Trap 6 (Invalid Opcode). Because we are now taking zero traps during V8086 mode, we can't do the "BOP lookahead", so the only trap we do get is when we hit the BOP/invalid opcode itself.
[NTOS]: Multiple fixes to V8086 opcode emulation code that I noticed while looking through the source. Also multiple fixes to VDM code.
This change will only impact real hardware and VMWare, since QEMU does not support VME. On VMWare, performance increased up to 400% during bootup (80 million cycles instead of 300 million, in one test).
svn path=/trunk/; revision=45282
2010-01-27 05:34:38 +00:00
|
|
|
/* Check for VME support */
|
|
|
|
ASSERT(KeI386VirtualIntExtensions == FALSE);
|
2010-01-10 15:40:00 +00:00
|
|
|
|
|
|
|
/* Enable interrupts */
|
|
|
|
KiVdmSetVdmEFlags(EFLAGS_INTERRUPT_MASK);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Skip instruction */
|
2010-01-13 03:43:03 +00:00
|
|
|
TrapFrame->Eip += KiVdmGetInstructionSize(Flags);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Done */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* MASTER OPCODE HANDLER ******************************************************/
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmHandleOpcode(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
|
|
|
ULONG Eip;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Get flat EIP of the *current* instruction (not the original EIP) */
|
|
|
|
Eip = (TrapFrame->SegCs << 4) + TrapFrame->Eip;
|
2010-01-13 03:43:03 +00:00
|
|
|
Eip += KiVdmGetInstructionSize(Flags) - 1;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Read the opcode entry */
|
|
|
|
switch (*(PUCHAR)Eip)
|
|
|
|
{
|
|
|
|
case 0xF: return KiCallVdmHandler(F);
|
|
|
|
case 0x26: return KiCallVdmPrefixHandler(PFX_FLAG_ES);
|
|
|
|
case 0x2E: return KiCallVdmPrefixHandler(PFX_FLAG_CS);
|
|
|
|
case 0x36: return KiCallVdmPrefixHandler(PFX_FLAG_SS);
|
|
|
|
case 0x3E: return KiCallVdmPrefixHandler(PFX_FLAG_DS);
|
|
|
|
case 0x64: return KiCallVdmPrefixHandler(PFX_FLAG_FS);
|
|
|
|
case 0x65: return KiCallVdmPrefixHandler(PFX_FLAG_GS);
|
|
|
|
case 0x66: return KiCallVdmPrefixHandler(PFX_FLAG_OPER32);
|
|
|
|
case 0x67: return KiCallVdmPrefixHandler(PFX_FLAG_ADDR32);
|
|
|
|
case 0xF0: return KiCallVdmPrefixHandler(PFX_FLAG_LOCK);
|
|
|
|
case 0xF2: return KiCallVdmPrefixHandler(PFX_FLAG_REPNE);
|
|
|
|
case 0xF3: return KiCallVdmPrefixHandler(PFX_FLAG_REP);
|
|
|
|
case 0x6C: return KiCallVdmHandler(INSB);
|
|
|
|
case 0x6D: return KiCallVdmHandler(INSW);
|
|
|
|
case 0x6E: return KiCallVdmHandler(OUTSB);
|
|
|
|
case 0x6F: return KiCallVdmHandler(OUTSW);
|
|
|
|
case 0x98: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xD8: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xD9: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xDA: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xDB: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xDC: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xDD: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xDE: return KiCallVdmHandler(NPX);
|
|
|
|
case 0xDF: return KiCallVdmHandler(NPX);
|
|
|
|
case 0x9C: return KiCallVdmHandler(PUSHF);
|
|
|
|
case 0x9D: return KiCallVdmHandler(POPF);
|
|
|
|
case 0xCD: return KiCallVdmHandler(INTnn);
|
|
|
|
case 0xCE: return KiCallVdmHandler(INTO);
|
2013-03-10 11:44:04 +00:00
|
|
|
case 0xCF: return KiCallVdmHandler(IRET);
|
|
|
|
case 0xE4: return KiCallVdmHandler(INBimm);
|
2010-01-10 15:40:00 +00:00
|
|
|
case 0xE5: return KiCallVdmHandler(INWimm);
|
|
|
|
case 0xE6: return KiCallVdmHandler(OUTBimm);
|
2013-03-10 11:44:04 +00:00
|
|
|
case 0xE7: return KiCallVdmHandler(OUTWimm);
|
2010-01-10 15:40:00 +00:00
|
|
|
case 0xEC: return KiCallVdmHandler(INB);
|
|
|
|
case 0xED: return KiCallVdmHandler(INW);
|
|
|
|
case 0xEE: return KiCallVdmHandler(OUTB);
|
|
|
|
case 0xEF: return KiCallVdmHandler(OUTW);
|
|
|
|
case 0xF4: return KiCallVdmHandler(HLT);
|
|
|
|
case 0xFA: return KiCallVdmHandler(CLI);
|
|
|
|
case 0xFB: return KiCallVdmHandler(STI);
|
2019-01-21 08:35:20 +00:00
|
|
|
default:
|
|
|
|
DPRINT1("Unhandled instruction: 0x%02x.\n", *(PUCHAR)Eip);
|
|
|
|
return KiCallVdmHandler(INV);
|
2013-03-10 11:44:04 +00:00
|
|
|
}
|
2010-01-10 15:40:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* PREFIX HANDLER *************************************************************/
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
KiVdmOpcodePrefix(IN PKTRAP_FRAME TrapFrame,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
|
|
|
/* Increase instruction size */
|
|
|
|
Flags++;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* Handle the next opcode */
|
|
|
|
return KiVdmHandleOpcode(TrapFrame, Flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TRAP HANDLER ***************************************************************/
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
FASTCALL
|
|
|
|
Ki386HandleOpcodeV86(IN PKTRAP_FRAME TrapFrame)
|
|
|
|
{
|
|
|
|
/* Clean up */
|
|
|
|
TrapFrame->Eip &= 0xFFFF;
|
|
|
|
TrapFrame->HardwareEsp &= 0xFFFF;
|
|
|
|
|
|
|
|
/* We start with only 1 byte per instruction */
|
|
|
|
return KiVdmHandleOpcode(TrapFrame, 1);
|
|
|
|
}
|
|
|
|
|
2010-01-11 18:26:46 +00:00
|
|
|
ULONG_PTR
|
2010-01-11 03:47:17 +00:00
|
|
|
FASTCALL
|
|
|
|
KiExitV86Mode(IN PKTRAP_FRAME TrapFrame)
|
|
|
|
{
|
2019-12-01 19:36:13 +00:00
|
|
|
PKPCR Pcr = KeGetPcr();
|
2014-04-13 12:04:13 +00:00
|
|
|
ULONG_PTR StackFrameUnaligned;
|
2010-01-11 03:47:17 +00:00
|
|
|
PKV8086_STACK_FRAME StackFrame;
|
|
|
|
PKTHREAD Thread;
|
|
|
|
PKV86_FRAME V86Frame;
|
|
|
|
PFX_SAVE_AREA NpxFrame;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Get the stack frame back */
|
2014-04-13 12:04:13 +00:00
|
|
|
StackFrameUnaligned = TrapFrame->Esi;
|
|
|
|
StackFrame = (PKV8086_STACK_FRAME)(ROUND_UP(StackFrameUnaligned - 4, 16) + 4);
|
2010-01-11 03:47:17 +00:00
|
|
|
V86Frame = &StackFrame->V86Frame;
|
|
|
|
NpxFrame = &StackFrame->NpxArea;
|
2014-04-13 12:04:13 +00:00
|
|
|
ASSERT((ULONG_PTR)NpxFrame % 16 == 0);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Copy the FPU frame back */
|
|
|
|
Thread = KeGetCurrentThread();
|
|
|
|
RtlCopyMemory(KiGetThreadNpxArea(Thread), NpxFrame, sizeof(FX_SAVE_AREA));
|
|
|
|
|
|
|
|
/* Set initial stack back */
|
|
|
|
Thread->InitialStack = (PVOID)((ULONG_PTR)V86Frame->ThreadStack + sizeof(FX_SAVE_AREA));
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Set ESP0 back in the KTSS */
|
2019-12-01 19:36:13 +00:00
|
|
|
Pcr->TSS->Esp0 = (ULONG_PTR)Thread->InitialStack;
|
|
|
|
Pcr->TSS->Esp0 -= sizeof(KTRAP_FRAME) - FIELD_OFFSET(KTRAP_FRAME, V86Es);
|
|
|
|
Pcr->TSS->Esp0 -= NPX_FRAME_LENGTH;
|
2010-01-11 03:47:17 +00:00
|
|
|
|
|
|
|
/* Restore TEB addresses */
|
|
|
|
Thread->Teb = V86Frame->ThreadTeb;
|
2013-03-10 11:44:04 +00:00
|
|
|
KiSetTebBase(KeGetPcr(), V86Frame->ThreadTeb);
|
2010-01-11 03:47:17 +00:00
|
|
|
|
2010-01-13 21:31:55 +00:00
|
|
|
/* Enable interrupts and return a pointer to the trap frame */
|
2010-01-11 03:47:17 +00:00
|
|
|
_enable();
|
2014-04-13 12:04:13 +00:00
|
|
|
return StackFrameUnaligned;
|
2010-01-11 03:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FASTCALL
|
2014-04-13 12:04:13 +00:00
|
|
|
KiEnterV86Mode(IN ULONG_PTR StackFrameUnaligned)
|
2010-01-11 03:47:17 +00:00
|
|
|
{
|
|
|
|
PKTHREAD Thread;
|
2014-04-13 12:04:13 +00:00
|
|
|
PKV8086_STACK_FRAME StackFrame = (PKV8086_STACK_FRAME)(ROUND_UP(StackFrameUnaligned - 4, 16) + 4);
|
2010-01-11 03:47:17 +00:00
|
|
|
PKTRAP_FRAME TrapFrame = &StackFrame->TrapFrame;
|
|
|
|
PKV86_FRAME V86Frame = &StackFrame->V86Frame;
|
|
|
|
PFX_SAVE_AREA NpxFrame = &StackFrame->NpxArea;
|
|
|
|
|
2014-04-13 12:04:13 +00:00
|
|
|
ASSERT((ULONG_PTR)NpxFrame % 16 == 0);
|
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Build fake user-mode trap frame */
|
|
|
|
TrapFrame->SegCs = KGDT_R0_CODE | RPL_MASK;
|
|
|
|
TrapFrame->SegEs = TrapFrame->SegDs = TrapFrame->SegFs = TrapFrame->SegGs = 0;
|
|
|
|
TrapFrame->ErrCode = 0;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Get the current thread's initial stack */
|
|
|
|
Thread = KeGetCurrentThread();
|
|
|
|
V86Frame->ThreadStack = KiGetThreadNpxArea(Thread);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Save TEB addresses */
|
|
|
|
V86Frame->ThreadTeb = Thread->Teb;
|
2010-03-12 16:28:04 +00:00
|
|
|
V86Frame->PcrTeb = KeGetPcr()->NtTib.Self;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Save return EIP */
|
2010-01-11 18:26:46 +00:00
|
|
|
TrapFrame->Eip = (ULONG_PTR)Ki386BiosCallReturnAddress;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Save our stack (after the frames) */
|
2014-04-13 12:04:13 +00:00
|
|
|
TrapFrame->Esi = StackFrameUnaligned;
|
2010-01-11 03:47:17 +00:00
|
|
|
TrapFrame->Edi = (ULONG_PTR)_AddressOfReturnAddress() + 4;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Sanitize EFlags and enable interrupts */
|
|
|
|
TrapFrame->EFlags = __readeflags() & 0x60DD7;
|
|
|
|
TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Fill out the rest of the frame */
|
|
|
|
TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
|
|
|
|
TrapFrame->HardwareEsp = 0x11FFE;
|
|
|
|
TrapFrame->ExceptionList = EXCEPTION_CHAIN_END;
|
|
|
|
TrapFrame->Dr7 = 0;
|
2012-02-19 10:38:38 +00:00
|
|
|
|
|
|
|
/* Set some debug fields if trap debugging is enabled */
|
2012-04-18 13:39:19 +00:00
|
|
|
KiFillTrapFrameDebug(TrapFrame);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Disable interrupts */
|
|
|
|
_disable();
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Copy the thread's NPX frame */
|
|
|
|
RtlCopyMemory(NpxFrame, V86Frame->ThreadStack, sizeof(FX_SAVE_AREA));
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Clear exception list */
|
2010-03-12 16:28:04 +00:00
|
|
|
KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Set new ESP0 */
|
|
|
|
KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Set new initial stack */
|
|
|
|
Thread->InitialStack = V86Frame;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Set VDM TEB */
|
|
|
|
Thread->Teb = (PTEB)TRAMPOLINE_TEB;
|
2013-03-10 11:44:04 +00:00
|
|
|
KiSetTebBase(KeGetPcr(), (PVOID)TRAMPOLINE_TEB);
|
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Enable interrupts */
|
|
|
|
_enable();
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Start VDM execution */
|
|
|
|
NtVdmControl(VdmStartExecution, NULL);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-11 03:47:17 +00:00
|
|
|
/* Exit to V86 mode */
|
|
|
|
KiEoiHelper(TrapFrame);
|
|
|
|
}
|
2012-02-18 23:59:31 +00:00
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
Ke386SetIOPL(VOID)
|
|
|
|
{
|
|
|
|
|
|
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
|
|
PKPROCESS Process = Thread->ApcState.Process;
|
|
|
|
PKTRAP_FRAME TrapFrame;
|
|
|
|
CONTEXT Context;
|
|
|
|
|
|
|
|
/* IOPL was enabled for this process/thread */
|
|
|
|
Process->Iopl = TRUE;
|
|
|
|
Thread->Iopl = TRUE;
|
|
|
|
|
|
|
|
/* Get the trap frame on exit */
|
|
|
|
TrapFrame = KeGetTrapFrame(Thread);
|
|
|
|
|
|
|
|
/* Convert to a context */
|
|
|
|
Context.ContextFlags = CONTEXT_CONTROL;
|
|
|
|
KeTrapFrameToContext(TrapFrame, NULL, &Context);
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2012-02-18 23:59:31 +00:00
|
|
|
/* Set the IOPL flag */
|
|
|
|
Context.EFlags |= EFLAGS_IOPL;
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2012-02-18 23:59:31 +00:00
|
|
|
/* Convert back to a trap frame */
|
|
|
|
KeContextToTrapFrame(&Context, NULL, TrapFrame, CONTEXT_CONTROL, UserMode);
|
|
|
|
}
|
2013-03-10 11:44:04 +00:00
|
|
|
|
2010-01-10 15:40:00 +00:00
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
Ke386CallBios(IN ULONG Int,
|
|
|
|
OUT PCONTEXT Context)
|
|
|
|
{
|
|
|
|
PUCHAR Trampoline = (PUCHAR)TRAMPOLINE_BASE;
|
|
|
|
PTEB VdmTeb = (PTEB)TRAMPOLINE_TEB;
|
|
|
|
PVDM_TIB VdmTib = (PVDM_TIB)TRAMPOLINE_TIB;
|
|
|
|
ULONG ContextSize = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
|
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
|
|
PKTSS Tss = KeGetPcr()->TSS;
|
|
|
|
PKPROCESS Process = Thread->ApcState.Process;
|
|
|
|
PVDM_PROCESS_OBJECTS VdmProcessObjects;
|
|
|
|
USHORT OldOffset, OldBase;
|
|
|
|
|
|
|
|
/* Start with a clean TEB */
|
|
|
|
RtlZeroMemory(VdmTeb, sizeof(TEB));
|
|
|
|
|
|
|
|
/* Write the interrupt and bop */
|
|
|
|
*Trampoline++ = 0xCD;
|
|
|
|
*Trampoline++ = (UCHAR)Int;
|
|
|
|
*(PULONG)Trampoline = TRAMPOLINE_BOP;
|
|
|
|
|
|
|
|
/* Setup the VDM TEB and TIB */
|
|
|
|
VdmTeb->Vdm = (PVOID)TRAMPOLINE_TIB;
|
|
|
|
RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
|
|
|
|
VdmTib->Size = sizeof(VDM_TIB);
|
|
|
|
|
|
|
|
/* Set a blank VDM state */
|
|
|
|
*VdmState = 0;
|
|
|
|
|
|
|
|
/* Copy the context */
|
|
|
|
RtlCopyMemory(&VdmTib->VdmContext, Context, ContextSize);
|
|
|
|
VdmTib->VdmContext.SegCs = (ULONG_PTR)Trampoline >> 4;
|
|
|
|
VdmTib->VdmContext.SegSs = (ULONG_PTR)Trampoline >> 4;
|
|
|
|
VdmTib->VdmContext.Eip = 0;
|
|
|
|
VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG_PTR);
|
|
|
|
VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
|
|
|
|
VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
|
|
|
|
|
|
|
|
/* This can't be a real VDM process */
|
|
|
|
ASSERT(PsGetCurrentProcess()->VdmObjects == NULL);
|
|
|
|
|
|
|
|
/* Allocate VDM structure */
|
|
|
|
VdmProcessObjects = ExAllocatePoolWithTag(NonPagedPool,
|
|
|
|
sizeof(VDM_PROCESS_OBJECTS),
|
2021-12-26 19:58:28 +00:00
|
|
|
TAG_KERNEL);
|
2006-11-08 11:47:44 +00:00
|
|
|
if (!VdmProcessObjects) return STATUS_NO_MEMORY;
|
|
|
|
|
|
|
|
/* Set it up */
|
|
|
|
RtlZeroMemory(VdmProcessObjects, sizeof(VDM_PROCESS_OBJECTS));
|
|
|
|
VdmProcessObjects->VdmTib = VdmTib;
|
|
|
|
PsGetCurrentProcess()->VdmObjects = VdmProcessObjects;
|
|
|
|
|
|
|
|
/* Set the system affinity for the current thread */
|
|
|
|
KeSetSystemAffinityThread(1);
|
|
|
|
|
|
|
|
/* Make sure there's space for two IOPMs, then copy & clear the current */
|
2008-10-22 13:24:01 +00:00
|
|
|
ASSERT(((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / 8].LimitLow >=
|
|
|
|
(0x2000 + IOPM_OFFSET - 1));
|
2019-11-24 21:56:55 +00:00
|
|
|
RtlCopyMemory(Ki386IopmSaveArea, &Tss->IoMaps[0].IoMap, IOPM_SIZE);
|
|
|
|
RtlZeroMemory(&Tss->IoMaps[0].IoMap, IOPM_SIZE);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Save the old offset and base, and set the new ones */
|
|
|
|
OldOffset = Process->IopmOffset;
|
|
|
|
OldBase = Tss->IoMapBase;
|
|
|
|
Process->IopmOffset = (USHORT)IOPM_OFFSET;
|
|
|
|
Tss->IoMapBase = (USHORT)IOPM_OFFSET;
|
|
|
|
|
|
|
|
/* Switch stacks and work the magic */
|
|
|
|
Ki386SetupAndExitToV86Mode(VdmTeb);
|
|
|
|
|
|
|
|
/* Restore IOPM */
|
2019-11-24 21:56:55 +00:00
|
|
|
RtlCopyMemory(&Tss->IoMaps[0].IoMap, Ki386IopmSaveArea, IOPM_SIZE);
|
2006-11-08 11:47:44 +00:00
|
|
|
Process->IopmOffset = OldOffset;
|
|
|
|
Tss->IoMapBase = OldBase;
|
|
|
|
|
|
|
|
/* Restore affinity */
|
|
|
|
KeRevertToUserAffinityThread();
|
|
|
|
|
|
|
|
/* Restore context */
|
|
|
|
RtlCopyMemory(Context, &VdmTib->VdmContext, ContextSize);
|
|
|
|
Context->ContextFlags = CONTEXT_FULL;
|
|
|
|
|
|
|
|
/* Free VDM objects */
|
2021-12-26 19:58:28 +00:00
|
|
|
ExFreePoolWithTag(PsGetCurrentProcess()->VdmObjects, TAG_KERNEL);
|
2006-11-08 11:47:44 +00:00
|
|
|
PsGetCurrentProcess()->VdmObjects = NULL;
|
|
|
|
|
|
|
|
/* Return status */
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2008-11-02 11:48:24 +00:00
|
|
|
/*
|
2010-05-05 23:06:32 +00:00
|
|
|
* @implemented
|
2008-11-02 11:48:24 +00:00
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
Ke386IoSetAccessProcess(IN PKPROCESS Process,
|
2010-05-05 23:06:32 +00:00
|
|
|
IN ULONG MapNumber)
|
2008-11-02 11:48:24 +00:00
|
|
|
{
|
2010-05-05 23:06:32 +00:00
|
|
|
USHORT MapOffset;
|
|
|
|
PKPRCB Prcb;
|
|
|
|
KAFFINITY TargetProcessors;
|
|
|
|
|
|
|
|
if(MapNumber > IOPM_COUNT)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
MapOffset = KiComputeIopmOffset(MapNumber);
|
|
|
|
|
|
|
|
Process->IopmOffset = MapOffset;
|
|
|
|
|
|
|
|
TargetProcessors = Process->ActiveProcessors;
|
|
|
|
Prcb = KeGetCurrentPrcb();
|
|
|
|
if (TargetProcessors & Prcb->SetMember)
|
|
|
|
KeGetPcr()->TSS->IoMapBase = MapOffset;
|
|
|
|
|
|
|
|
return TRUE;
|
2008-11-02 11:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-05 23:06:32 +00:00
|
|
|
* @implemented
|
2008-11-02 11:48:24 +00:00
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
2010-05-05 23:06:32 +00:00
|
|
|
Ke386SetIoAccessMap(IN ULONG MapNumber,
|
|
|
|
IN PKIO_ACCESS_MAP IopmBuffer)
|
2008-11-02 11:48:24 +00:00
|
|
|
{
|
2010-05-05 23:06:32 +00:00
|
|
|
PKPROCESS CurrentProcess;
|
|
|
|
PKPRCB Prcb;
|
|
|
|
PVOID pt;
|
|
|
|
|
|
|
|
if ((MapNumber > IOPM_COUNT) || (MapNumber == IO_ACCESS_MAP_NONE))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
Prcb = KeGetCurrentPrcb();
|
|
|
|
|
|
|
|
// Copy the IOP map and load the map for the current process.
|
|
|
|
pt = &(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
|
|
|
|
RtlMoveMemory(pt, (PVOID)IopmBuffer, IOPM_SIZE);
|
|
|
|
CurrentProcess = Prcb->CurrentThread->ApcState.Process;
|
|
|
|
KeGetPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
|
|
|
|
|
|
|
|
return TRUE;
|
2008-11-02 11:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-05 23:06:32 +00:00
|
|
|
* @implemented
|
2008-11-02 11:48:24 +00:00
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
2010-05-05 23:06:32 +00:00
|
|
|
Ke386QueryIoAccessMap(IN ULONG MapNumber,
|
|
|
|
IN PKIO_ACCESS_MAP IopmBuffer)
|
2008-11-02 11:48:24 +00:00
|
|
|
{
|
2010-05-05 23:06:32 +00:00
|
|
|
ULONG i;
|
|
|
|
PVOID Map;
|
|
|
|
PUCHAR p;
|
|
|
|
|
|
|
|
if (MapNumber > IOPM_COUNT)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (MapNumber == IO_ACCESS_MAP_NONE)
|
|
|
|
{
|
|
|
|
// no access, simply return a map of all 1s
|
|
|
|
p = (PUCHAR)IopmBuffer;
|
|
|
|
for (i = 0; i < IOPM_SIZE; i++) {
|
|
|
|
p[i] = (UCHAR)-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// copy the bits
|
|
|
|
Map = (PVOID)&(KeGetPcr()->TSS->IoMaps[MapNumber-1].IoMap);
|
|
|
|
RtlMoveMemory((PVOID)IopmBuffer, Map, IOPM_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
2008-11-02 11:48:24 +00:00
|
|
|
}
|