[NTOS]: Rewrite BIOS Call (V8086) Entry/Exit routines in C. Only 4 lines of ASM stub remain. This wasn't fun... the stack dancing alone gives you headaches. Who ever thought of dynamically sized trap frames!

svn path=/trunk/; revision=45037
This commit is contained in:
Sir Richard 2010-01-11 03:47:17 +00:00
parent 2741b8149a
commit 38de4a0a1c
5 changed files with 167 additions and 178 deletions

View file

@ -139,6 +139,27 @@ typedef union _KTRAP_EXIT_SKIP_BITS
return TRUE; \
}
C_ASSERT(NPX_FRAME_LENGTH == sizeof(FX_SAVE_AREA));
//
// Local parameters
//
typedef struct _KV86_FRAME
{
PVOID ThreadStack;
PVOID ThreadTeb;
PVOID PcrTeb;
} KV86_FRAME, *PKV86_FRAME;
//
// Virtual Stack Frame
//
typedef struct _KV8086_STACK_FRAME
{
KTRAP_FRAME TrapFrame;
FX_SAVE_AREA NpxArea;
KV86_FRAME V86Frame;
} KV8086_STACK_FRAME, *PKV8086_STACK_FRAME;
//
// Registers an interrupt handler with an IDT vector
@ -382,6 +403,12 @@ Ki386HandleOpcodeV86(
IN PKTRAP_FRAME TrapFrame
);
VOID
FASTCALL
KiEoiHelper(
IN PKTRAP_FRAME TrapFrame
);
//
// Global x86 only Kernel data
//
@ -409,6 +436,14 @@ extern VOID NTAPI ExpInterlockedPopEntrySListFault(VOID);
extern VOID __cdecl CopyParams(VOID);
extern VOID __cdecl ReadBatch(VOID);
extern VOID __cdecl FrRestore(VOID);
extern VOID Ki386BiosCallReturnAddress(VOID);
PFX_SAVE_AREA
FORCEINLINE
KiGetThreadNpxArea(IN PKTHREAD Thread)
{
return (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack - sizeof(FX_SAVE_AREA));
}
//
// Sanitizes a selector

View file

@ -137,13 +137,6 @@ KiVdmTrap(IN PKTRAP_FRAME TrapFrame)
((KiUserTrap(TrapFrame)) && (PsGetCurrentProcess()->VdmObjects)));
}
PFX_SAVE_AREA
FORCEINLINE
KiGetThreadNpxArea(IN PKTHREAD Thread)
{
return (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack - sizeof(FX_SAVE_AREA));
}
VOID
FORCEINLINE
KiTrapFrameFromPushaStack(IN PKTRAP_FRAME TrapFrame)

View file

@ -18,177 +18,20 @@
.func Ki386SetupAndExitToV86Mode@4
_Ki386SetupAndExitToV86Mode@4:
/* Save nonvolatiles */
push ebp
push ebx
push esi
push edi
/* Give us a little stack */
sub esp, 12
mov ecx, esp
/* Go past the KTRAP_FRAME and NPX Frame and set a new frame in EAX */
sub esp, NPX_FRAME_LENGTH
and esp, ~15
sub esp, KTRAP_FRAME_LENGTH
mov eax, esp
/* Create a fake user-mode frame */
mov dword ptr [eax+KTRAP_FRAME_CS], KGDT_R0_CODE + RPL_MASK
mov dword ptr [eax+KTRAP_FRAME_ES], 0
mov dword ptr [eax+KTRAP_FRAME_DS], 0
mov dword ptr [eax+KTRAP_FRAME_FS], 0
mov dword ptr [eax+KTRAP_FRAME_GS], 0
mov dword ptr [eax+KTRAP_FRAME_ERROR_CODE], 0
/* Get the current thread's initial stack */
mov ebx, [fs:KPCR_SELF]
mov edi, [ebx+KPCR_CURRENT_THREAD]
mov edx, [edi+KTHREAD_INITIAL_STACK]
sub edx, NPX_FRAME_LENGTH
/* Save it on our stack, as well as the real TEB addresses */
mov [ecx], edx
mov edx, [edi+KTHREAD_TEB]
mov [ecx+4], edx
mov edx, [fs:KPCR_TEB]
mov [ecx+8] , edx
/* Set our ESP in ESI, and the return function in EIP */
mov edi, offset _Ki386BiosCallReturnAddress
mov [eax+KTRAP_FRAME_ESI], ecx
mov [eax+KTRAP_FRAME_EIP], edi
/* Push the flags and sanitize them */
pushfd
pop edi
and edi, 0x60DD7
or edi, EFLAGS_INTERRUPT_MASK
/* Set SS and ESP, and fill out the rest of the frame */
mov dword ptr [eax+KTRAP_FRAME_SS], KGDT_R3_DATA + RPL_MASK;
mov dword ptr [eax+KTRAP_FRAME_ESP], 0x11FFE;
mov dword ptr [eax+KTRAP_FRAME_EFLAGS], edi
mov dword ptr [eax+KTRAP_FRAME_EXCEPTION_LIST], -1
mov dword ptr [eax+KTRAP_FRAME_PREVIOUS_MODE], -1
mov dword ptr [eax+KTRAP_FRAME_DR7], 0
mov dword ptr [eax+KTRAP_FRAME_DEBUGARGMARK], 0xBADB0D00
/* Jump past the frame now */
add eax, KTRAP_FRAME_LENGTH
cli
/* Save the current stack */
push ecx
/* Get the current thread's intial stack again */
mov edi, [ebx+KPCR_CURRENT_THREAD]
mov esi, [edi+KTHREAD_INITIAL_STACK]
sub esi, NPX_FRAME_LENGTH
/* Set the size of the copy, and the destination, and copy the NPX frame */
mov ecx, NPX_FRAME_LENGTH / 4
mov edi, eax
rep movsd
/* Restore stack */
pop ecx
/* Get the current thread and TSS */
mov edi, [ebx+KPCR_CURRENT_THREAD]
mov esi, [ebx+KPCR_TSS]
/* Bias the V86 vrame */
sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
/* Set exception list and new ESP */
mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
mov [esi+KTSS_ESP0], eax
/* Now skip past the NPX frame and V86 fields and set this as the intial stack */
add eax, NPX_FRAME_LENGTH + (KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS)
mov [edi+KTHREAD_INITIAL_STACK], eax
/* Setup our fake TEB pointer */
mov eax, [ecx+0x20]
mov [fs:KPCR_TEB], eax
mov [edi+KTHREAD_TEB], eax
/* Setup the descriptors for the fake TEB */
mov ebx, [fs:KPCR_GDT]
mov [ebx+0x3A], ax
shr eax, 16
mov [ebx+0x3C], al
mov [ebx+0x3F], ah
sti
/*
* Start VDM execution. This will save this fake 32-bit KTRAP_FRAME and
* initialize a real 16-bit VDM context frame
*/
push 0
push 0 // VdmStartExecution
call _NtVdmControl@8
/* Exit to V86 mode */
mov ebp, esp
jmp _Kei386EoiHelper@0
/* Enter V8086 mode */
pushad
call @KiEnterV86Mode@0
.endfunc
.globl _Ki386BiosCallReturnAddress
.func Ki386BiosCallReturnAddress
_Ki386BiosCallReturnAddress:
/* Get the PCR */
mov eax, [fs:KPCR_SELF]
/* Get NPX destination */
mov edi, [ebp+KTRAP_FRAME_ESI]
mov edi, [edi]
/* Get initial stack */
mov ecx, [eax+KPCR_CURRENT_THREAD]
mov esi, [ecx+KTHREAD_INITIAL_STACK]
sub esi, NPX_FRAME_LENGTH
/* Set length and copy the NPX frame */
mov ecx, NPX_FRAME_LENGTH / 4
rep movsd
/* Restore stack */
mov esp, [ebp+KTRAP_FRAME_ESI]
add esp, 4
/* Set initial stack */
mov ecx, [eax+KPCR_CURRENT_THREAD]
mov [ecx+KTHREAD_INITIAL_STACK], edi
/* Get TSS and set the ESP 0 */
mov eax, [eax+KPCR_TSS]
sub edi, NPX_FRAME_LENGTH + (KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS)
mov [eax+KTSS_ESP0], edi
/* Restore KTHREAD TEB in EDX */
pop edx
mov [ecx+KTHREAD_TEB], edx
/* Restore PCR TEB in EDX */
pop edx
mov [fs:KPCR_TEB], edx
/* Setup the descriptors for the real TEB */
mov ebx, [fs:KPCR_GDT]
mov [ebx+0x3A], dx
shr edx, 16
mov [ebx+0x3C], dl
mov [ebx+0x3F], dh
/* Enable interrupts and pop back non-volatiles */
sti
pop edi
pop esi
pop ebx
pop ebp
ret 4
/* Exit V8086 mode */
mov ecx, ebp
call @KiExitV86Mode@4
mov esp, eax
popad
.endfunc

View file

@ -19,8 +19,6 @@ ULONG KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE;
ULONG KeI386EFlagsOrMaskV86 = EFLAGS_INTERRUPT_MASK;
PVOID Ki386IopmSaveArea;
BOOLEAN KeI386VirtualIntExtensions = FALSE;
#if 1
const PULONG KiNtVdmState = (PULONG)FIXED_NTVDMSTATE_LINEAR_PC_AT;
/* UNHANDLED OPCODES **********************************************************/
@ -431,8 +429,128 @@ Ki386HandleOpcodeV86(IN PKTRAP_FRAME TrapFrame)
return KiVdmHandleOpcode(TrapFrame, 1);
}
ULONG_PTR
FASTCALL
KiExitV86Mode(IN PKTRAP_FRAME TrapFrame)
{
PKV8086_STACK_FRAME StackFrame;
PKGDTENTRY GdtEntry;
PKTHREAD Thread;
PKTRAP_FRAME PmTrapFrame;
PKV86_FRAME V86Frame;
PFX_SAVE_AREA NpxFrame;
/* Get the stack frame back */
StackFrame = CONTAINING_RECORD(TrapFrame->Esi, KV8086_STACK_FRAME, V86Frame);
PmTrapFrame = &StackFrame->TrapFrame;
V86Frame = &StackFrame->V86Frame;
NpxFrame = &StackFrame->NpxArea;
/* 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));
/* Set ESP0 back in the KTSS */
KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&PmTrapFrame->V86Es;
/* Restore TEB addresses */
Thread->Teb = V86Frame->ThreadTeb;
KeGetPcr()->Tib.Self = V86Frame->PcrTeb;
/* Setup real TEB descriptor */
GdtEntry = &((PKIPCR)KeGetPcr())->GDT[KGDT_R3_TEB / sizeof(KGDTENTRY)];
GdtEntry->BaseLow = (USHORT)((ULONG_PTR)Thread->Teb & 0xFFFF);
GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)Thread->Teb >> 16);
GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)Thread->Teb >> 24);
/* Enable interrupts and pop back non-volatiles */
_enable();
return TrapFrame->Edi;
}
VOID
FASTCALL
KiEnterV86Mode(VOID)
{
PKTHREAD Thread;
PKGDTENTRY GdtEntry;
KV8086_STACK_FRAME StackFrameBuffer;
PKV8086_STACK_FRAME StackFrame = &StackFrameBuffer;
PKTRAP_FRAME TrapFrame = &StackFrame->TrapFrame;
PKV86_FRAME V86Frame = &StackFrame->V86Frame;
PFX_SAVE_AREA NpxFrame = &StackFrame->NpxArea;
/* 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;
/* Get the current thread's initial stack */
Thread = KeGetCurrentThread();
V86Frame->ThreadStack = KiGetThreadNpxArea(Thread);
/* Save TEB addresses */
V86Frame->ThreadTeb = Thread->Teb;
V86Frame->PcrTeb = KeGetPcr()->Tib.Self;
/* Save return EIP */
TrapFrame->Eip = (ULONG_PTR)Ki386BiosCallReturnAddress;
/* Save our stack (after the frames) */
TrapFrame->Esi = (ULONG_PTR)V86Frame;
TrapFrame->Edi = (ULONG_PTR)_AddressOfReturnAddress() + 4;
/* Sanitize EFlags and enable interrupts */
TrapFrame->EFlags = __readeflags() & 0x60DD7;
TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
/* 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;
//TrapFrame->DbgArgMark = 0xBADB0D00;
TrapFrame->PreviousPreviousMode = -1;
/* Disable interrupts */
_disable();
/* Copy the thread's NPX frame */
RtlCopyMemory(NpxFrame, V86Frame->ThreadStack, sizeof(FX_SAVE_AREA));
/* Clear exception list */
KeGetPcr()->Tib.ExceptionList = EXCEPTION_CHAIN_END;
/* Set new ESP0 */
KeGetPcr()->TSS->Esp0 = (ULONG_PTR)&TrapFrame->V86Es;
/* Set new initial stack */
Thread->InitialStack = V86Frame;
/* Set VDM TEB */
Thread->Teb = (PTEB)TRAMPOLINE_TEB;
KeGetPcr()->Tib.Self = (PVOID)TRAMPOLINE_TEB;
/* Setup VDM TEB descriptor */
GdtEntry = &((PKIPCR)KeGetPcr())->GDT[KGDT_R3_TEB / sizeof(KGDTENTRY)];
GdtEntry->BaseLow = (USHORT)((ULONG_PTR)TRAMPOLINE_TEB & 0xFFFF);
GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((ULONG_PTR)TRAMPOLINE_TEB >> 16);
GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((ULONG_PTR)TRAMPOLINE_TEB >> 24);
/* Enable interrupts */
_enable();
/* Start VDM execution */
NtVdmControl(VdmStartExecution, NULL);
/* Exit to V86 mode */
KiEoiHelper(TrapFrame);
}
/* PUBLIC FUNCTIONS ***********************************************************/
#endif
/*
* @implemented

View file

@ -50,7 +50,7 @@ VdmSwapContext(IN PKTRAP_FRAME TrapFrame,
/* Make sure that we're at APC_LEVEL and that this is a valid frame */
ASSERT(KeGetCurrentIrql() == APC_LEVEL);
ASSERT(TrapFrame->DbgArgMark == 0xBADB0D00);
//ASSERT(TrapFrame->DbgArgMark == 0xBADB0D00);
/* Check if this is a V86 frame */
if (TrapFrame->EFlags & EFLAGS_V86_MASK)