From 38de4a0a1cbd94ee328ad35779585c5e69485330 Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Mon, 11 Jan 2010 03:47:17 +0000 Subject: [PATCH] [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 --- reactos/ntoskrnl/include/internal/i386/ke.h | 35 ++++ reactos/ntoskrnl/include/internal/trap_x.h | 7 - reactos/ntoskrnl/ke/i386/v86m_sup.S | 177 ++------------------ reactos/ntoskrnl/ke/i386/v86vdm.c | 124 +++++++++++++- reactos/ntoskrnl/vdm/vdmexec.c | 2 +- 5 files changed, 167 insertions(+), 178 deletions(-) diff --git a/reactos/ntoskrnl/include/internal/i386/ke.h b/reactos/ntoskrnl/include/internal/i386/ke.h index abcaba72aef..200a843ce75 100644 --- a/reactos/ntoskrnl/include/internal/i386/ke.h +++ b/reactos/ntoskrnl/include/internal/i386/ke.h @@ -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 diff --git a/reactos/ntoskrnl/include/internal/trap_x.h b/reactos/ntoskrnl/include/internal/trap_x.h index 898bba033c1..a5191234207 100644 --- a/reactos/ntoskrnl/include/internal/trap_x.h +++ b/reactos/ntoskrnl/include/internal/trap_x.h @@ -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) diff --git a/reactos/ntoskrnl/ke/i386/v86m_sup.S b/reactos/ntoskrnl/ke/i386/v86m_sup.S index 17440234bcc..fd4d58b842f 100644 --- a/reactos/ntoskrnl/ke/i386/v86m_sup.S +++ b/reactos/ntoskrnl/ke/i386/v86m_sup.S @@ -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 + + diff --git a/reactos/ntoskrnl/ke/i386/v86vdm.c b/reactos/ntoskrnl/ke/i386/v86vdm.c index 2a95411ccf8..8e587bd29ba 100644 --- a/reactos/ntoskrnl/ke/i386/v86vdm.c +++ b/reactos/ntoskrnl/ke/i386/v86vdm.c @@ -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 diff --git a/reactos/ntoskrnl/vdm/vdmexec.c b/reactos/ntoskrnl/vdm/vdmexec.c index d54974ab6b5..067c1cc971d 100644 --- a/reactos/ntoskrnl/vdm/vdmexec.c +++ b/reactos/ntoskrnl/vdm/vdmexec.c @@ -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)