mirror of
https://github.com/reactos/reactos.git
synced 2024-11-07 15:10:53 +00:00
c16ad873a6
svn path=/branches/reactos-yarotows/; revision=46279
650 lines
14 KiB
ArmAsm
650 lines
14 KiB
ArmAsm
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/ke/i386/ctxswitch.S
|
|
* PURPOSE: Thread Context Switching
|
|
*
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
|
* Gregor Anich (FPU Code)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ndk/asm.h>
|
|
.intel_syntax noprefix
|
|
|
|
#define Ready 1
|
|
#define Running 2
|
|
#define WrDispatchInt 0x1F
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/*++
|
|
* KiSwapContextInternal
|
|
*
|
|
* The KiSwapContextInternal routine switches context to another thread.
|
|
*
|
|
* Params:
|
|
* ESI - Pointer to the KTHREAD to which the caller wishes to
|
|
* switch to.
|
|
* EDI - Pointer to the KTHREAD to which the caller wishes to
|
|
* switch from.
|
|
*
|
|
* Returns:
|
|
* None.
|
|
*
|
|
* Remarks:
|
|
* Absolutely all registers except ESP can be trampled here for maximum code flexibility.
|
|
*
|
|
*--*/
|
|
.globl @KiSwapContextInternal@0
|
|
.func @KiSwapContextInternal@0, @KiSwapContextInternal@0
|
|
@KiSwapContextInternal@0:
|
|
|
|
/* Save the IRQL */
|
|
push ecx
|
|
|
|
#ifdef CONFIG_SMP
|
|
GetSwapLock:
|
|
/* Acquire the swap lock */
|
|
cmp byte ptr [esi+KTHREAD_SWAP_BUSY], 0
|
|
jz NotBusy
|
|
pause
|
|
jmp GetSwapLock
|
|
NotBusy:
|
|
#endif
|
|
/* Increase context switches (use ES for lazy load) */
|
|
inc dword ptr es:[ebx+KPCR_CONTEXT_SWITCHES]
|
|
|
|
/* Save the Exception list */
|
|
push [ebx+KPCR_EXCEPTION_LIST]
|
|
|
|
/* Check for WMI */
|
|
cmp dword ptr [ebx+KPCR_PERF_GLOBAL_GROUP_MASK], 0
|
|
jnz WmiTrace
|
|
|
|
AfterTrace:
|
|
#ifdef CONFIG_SMP
|
|
#if DBG
|
|
/* Assert that we're on the right CPU */
|
|
mov cl, [esi+KTHREAD_NEXT_PROCESSOR]
|
|
cmp cl, [ebx+KPCR_PROCESSOR_NUMBER]
|
|
jnz WrongCpu
|
|
#endif
|
|
#endif
|
|
|
|
/* Get CR0 and save it */
|
|
mov ebp, cr0
|
|
mov edx, ebp
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Check NPX State */
|
|
cmp byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_LOADED
|
|
jz NpxLoaded
|
|
SetStack:
|
|
#endif
|
|
|
|
/* Set new stack */
|
|
mov [edi+KTHREAD_KERNEL_STACK], esp
|
|
|
|
/* Checking NPX, disable interrupts now */
|
|
mov eax, [esi+KTHREAD_INITIAL_STACK]
|
|
cli
|
|
|
|
/* Get the NPX State */
|
|
movzx ecx, byte ptr [esi+KTHREAD_NPX_STATE]
|
|
|
|
/* Clear the other bits, merge in CR0, merge in FPU CR0 bits and compare */
|
|
and edx, ~(CR0_MP + CR0_EM + CR0_TS)
|
|
or ecx, edx
|
|
or ecx, [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)]
|
|
cmp ebp, ecx
|
|
jnz NewCr0
|
|
|
|
StackOk:
|
|
/* Enable interrupts and set the current stack */
|
|
sti
|
|
mov esp, [esi+KTHREAD_KERNEL_STACK]
|
|
|
|
/* Check if address space switch is needed */
|
|
mov ebp, [esi+KTHREAD_APCSTATE_PROCESS]
|
|
mov eax, [edi+KTHREAD_APCSTATE_PROCESS]
|
|
cmp ebp, eax
|
|
jz SameProcess
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Get the active processors and XOR with the process' */
|
|
mov ecx, [ebx+KPCR_SET_MEMBER_COPY]
|
|
lock xor [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
|
|
lock xor [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
|
|
|
|
/* Assert change went ok */
|
|
#if DBG
|
|
test [ebp+KPROCESS_ACTIVE_PROCESSORS], ecx
|
|
jz WrongActiveCpu
|
|
test [eax+KPROCESS_ACTIVE_PROCESSORS], ecx
|
|
jnz WrongActiveCpu
|
|
#endif
|
|
#endif
|
|
|
|
/* Check if we need an LDT */
|
|
mov ecx, [ebp+KPROCESS_LDT_DESCRIPTOR0]
|
|
or ecx, [eax+KPROCESS_LDT_DESCRIPTOR0]
|
|
jnz LdtReload
|
|
|
|
UpdateCr3:
|
|
/* Switch address space */
|
|
mov eax, [ebp+KPROCESS_DIRECTORY_TABLE_BASE]
|
|
mov cr3, eax
|
|
|
|
SameProcess:
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Release swap lock */
|
|
and byte ptr [edi+KTHREAD_SWAP_BUSY], 0
|
|
#endif
|
|
|
|
/* Clear gs */
|
|
xor eax, eax
|
|
mov gs, ax
|
|
|
|
/* Set the TEB */
|
|
mov eax, [esi+KTHREAD_TEB]
|
|
mov [ebx+KPCR_TEB], eax
|
|
mov ecx, [ebx+KPCR_GDT]
|
|
mov [ecx+0x3A], ax
|
|
shr eax, 16
|
|
mov [ecx+0x3C], al
|
|
mov [ecx+0x3F], ah
|
|
|
|
/* Get stack pointer */
|
|
mov eax, [esi+KTHREAD_INITIAL_STACK]
|
|
|
|
/* Make space for the NPX Frame */
|
|
sub eax, NPX_FRAME_LENGTH
|
|
|
|
/* Check if this isn't V86 Mode, so we can bias the Esp0 */
|
|
test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
|
|
jnz NoAdjust
|
|
|
|
/* Bias esp */
|
|
sub eax, KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
|
|
|
|
NoAdjust:
|
|
|
|
/* Set new ESP0 */
|
|
mov ecx, [ebx+KPCR_TSS]
|
|
mov [ecx+KTSS_ESP0], eax
|
|
|
|
/* Set current IOPM offset in the TSS */
|
|
mov ax, [ebp+KPROCESS_IOPM_OFFSET]
|
|
mov [ecx+KTSS_IOMAPBASE], ax
|
|
|
|
/* Increase context switches */
|
|
inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
|
|
|
|
/* Restore exception list */
|
|
pop [ebx+KPCR_EXCEPTION_LIST]
|
|
|
|
/* Restore IRQL */
|
|
pop ecx
|
|
|
|
/* DPC shouldn't be active */
|
|
cmp byte ptr [ebx+KPCR_PRCB_DPC_ROUTINE_ACTIVE], 0
|
|
jnz BugCheckDpc
|
|
|
|
/* Check if kernel APCs are pending */
|
|
cmp byte ptr [esi+KTHREAD_PENDING_KERNEL_APC], 0
|
|
jnz CheckApc
|
|
|
|
/* No APCs, return */
|
|
xor eax, eax
|
|
ret
|
|
|
|
CheckApc:
|
|
|
|
/* Check if they're disabled */
|
|
cmp word ptr [esi+KTHREAD_SPECIAL_APC_DISABLE], 0
|
|
jnz ApcReturn
|
|
test cl, cl
|
|
jz ApcReturn
|
|
|
|
/* Request APC Delivery */
|
|
mov cl, APC_LEVEL
|
|
call @HalRequestSoftwareInterrupt@4
|
|
or eax, esp
|
|
|
|
ApcReturn:
|
|
|
|
/* Return with APC pending */
|
|
setz al
|
|
ret
|
|
|
|
LdtReload:
|
|
/* Check if it's empty */
|
|
mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR0]
|
|
test eax, eax
|
|
jz LoadLdt
|
|
|
|
/* Write the LDT Selector */
|
|
mov ecx, [ebx+KPCR_GDT]
|
|
mov [ecx+KGDT_LDT], eax
|
|
mov eax, [ebp+KPROCESS_LDT_DESCRIPTOR1]
|
|
mov [ecx+KGDT_LDT+4], eax
|
|
|
|
/* Write the INT21 handler */
|
|
mov ecx, [ebx+KPCR_IDT]
|
|
mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR0]
|
|
mov [ecx+0x108], eax
|
|
mov eax, [ebp+KPROCESS_INT21_DESCRIPTOR1]
|
|
mov [ecx+0x10C], eax
|
|
|
|
/* Save LDT Selector */
|
|
mov eax, KGDT_LDT
|
|
|
|
LoadLdt:
|
|
lldt ax
|
|
jmp UpdateCr3
|
|
|
|
NewCr0:
|
|
|
|
#if DBG
|
|
/* Assert NPX State */
|
|
test byte ptr [esi+KTHREAD_NPX_STATE], ~(NPX_STATE_NOT_LOADED)
|
|
jnz InvalidNpx
|
|
test dword ptr [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)], ~(CR0_PE + CR0_MP + CR0_EM + CR0_TS)
|
|
jnz InvalidNpx
|
|
#endif
|
|
|
|
/* Update CR0 */
|
|
mov cr0, ecx
|
|
jmp StackOk
|
|
|
|
#ifdef CONFIG_SMP
|
|
NpxLoaded:
|
|
|
|
/* Mask out FPU flags */
|
|
and edx, ~(CR0_MP + CR0_EM + CR0_TS)
|
|
|
|
/* Get the NPX Frame */
|
|
mov ecx, [edi+KTHREAD_INITIAL_STACK]
|
|
sub ecx, NPX_FRAME_LENGTH
|
|
|
|
/* Check if we have a new CR0 */
|
|
cmp ebp, edx
|
|
jz Cr0Equal
|
|
|
|
/* We do, update it */
|
|
mov cr0, edx
|
|
mov ebp, edx
|
|
|
|
Cr0Equal:
|
|
|
|
/* Save the NPX State */
|
|
fxsave [ecx]
|
|
mov byte ptr [edi+KTHREAD_NPX_STATE], NPX_STATE_NOT_LOADED
|
|
|
|
/* Clear the NPX Thread */
|
|
mov dword ptr [ebx+KPCR_NPX_THREAD], 0
|
|
|
|
/* Jump back */
|
|
jmp SetStack
|
|
#endif
|
|
|
|
WmiTrace:
|
|
|
|
/* No WMI support yet */
|
|
int 3
|
|
|
|
/* Jump back */
|
|
jmp AfterTrace
|
|
|
|
BugCheckDpc:
|
|
|
|
/* Bugcheck the machine, printing out the threads being switched */
|
|
mov eax, [edi+KTHREAD_INITIAL_STACK]
|
|
push 0
|
|
push eax
|
|
push esi
|
|
push edi
|
|
push ATTEMPTED_SWITCH_FROM_DPC
|
|
call _KeBugCheckEx@20
|
|
|
|
#if DBG
|
|
InvalidNpx:
|
|
int 3
|
|
WrongActiveCpu:
|
|
int 3
|
|
WrongCpu:
|
|
int 3
|
|
#endif
|
|
.endfunc
|
|
|
|
/*++
|
|
* KiSwapContext
|
|
*
|
|
* The KiSwapContext routine switches context to another thread.
|
|
*
|
|
* Params:
|
|
* TargetThread - Pointer to the KTHREAD to which the caller wishes to
|
|
* switch to.
|
|
*
|
|
* Returns:
|
|
* The WaitStatus of the Target Thread.
|
|
*
|
|
* Remarks:
|
|
* This is a wrapper around KiSwapContextInternal which will save all the
|
|
* non-volatile registers so that the Internal function can use all of
|
|
* them. It will also save the old current thread and set the new one.
|
|
*
|
|
* The calling thread does not return after KiSwapContextInternal until
|
|
* another thread switches to IT.
|
|
*
|
|
*--*/
|
|
.globl @KiSwapContext@8
|
|
.func @KiSwapContext@8, @KiSwapContext@8
|
|
@KiSwapContext@8:
|
|
|
|
/* Save 4 registers */
|
|
sub esp, 4 * 4
|
|
|
|
/* Save all the non-volatile ones */
|
|
mov [esp+12], ebx
|
|
mov [esp+8], esi
|
|
mov [esp+4], edi
|
|
mov [esp+0], ebp
|
|
|
|
/* Get the current KPCR */
|
|
mov ebx, fs:[KPCR_SELF]
|
|
|
|
/* Get the Current Thread */
|
|
mov edi, ecx
|
|
|
|
/* Get the New Thread */
|
|
mov esi, edx
|
|
|
|
/* Get the wait IRQL */
|
|
movzx ecx, byte ptr [edi+KTHREAD_WAIT_IRQL]
|
|
|
|
/* Do the swap with the registers correctly setup */
|
|
call @KiSwapContextInternal@0
|
|
|
|
/* Return the registers */
|
|
mov ebp, [esp+0]
|
|
mov edi, [esp+4]
|
|
mov esi, [esp+8]
|
|
mov ebx, [esp+12]
|
|
|
|
/* Clean stack */
|
|
add esp, 4 * 4
|
|
ret
|
|
.endfunc
|
|
|
|
/* DPC INTERRUPT HANDLER ******************************************************/
|
|
|
|
.globl _KiDispatchInterrupt@0
|
|
.func KiDispatchInterrupt@0
|
|
_KiDispatchInterrupt@0:
|
|
|
|
/* Preserve EBX */
|
|
push ebx
|
|
|
|
/* Get the PCR and disable interrupts */
|
|
mov ebx, PCR[KPCR_SELF]
|
|
cli
|
|
|
|
/* Check if we have to deliver DPCs, timers, or deferred threads */
|
|
mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
|
|
or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
|
|
or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
|
|
jz CheckQuantum
|
|
|
|
/* Save stack pointer and exception list, then clear it */
|
|
push ebp
|
|
push dword ptr [ebx+KPCR_EXCEPTION_LIST]
|
|
mov dword ptr [ebx+KPCR_EXCEPTION_LIST], -1
|
|
|
|
/* Save the stack and switch to the DPC Stack */
|
|
mov edx, esp
|
|
mov esp, [ebx+KPCR_PRCB_DPC_STACK]
|
|
push edx
|
|
|
|
/* Deliver DPCs */
|
|
mov ecx, [ebx+KPCR_PRCB]
|
|
call @KiRetireDpcList@4
|
|
|
|
/* Restore stack and exception list */
|
|
pop esp
|
|
pop dword ptr [ebx+KPCR_EXCEPTION_LIST]
|
|
pop ebp
|
|
|
|
CheckQuantum:
|
|
|
|
/* Re-enable interrupts */
|
|
sti
|
|
|
|
/* Check if we have quantum end */
|
|
cmp byte ptr [ebx+KPCR_PRCB_QUANTUM_END], 0
|
|
jnz QuantumEnd
|
|
|
|
/* Check if we have a thread to swap to */
|
|
cmp byte ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
|
|
je Return
|
|
|
|
/* Make space on the stack to save registers */
|
|
sub esp, 3 * 4
|
|
mov [esp+8], esi
|
|
mov [esp+4], edi
|
|
mov [esp+0], ebp
|
|
|
|
/* Get the current thread */
|
|
mov edi, [ebx+KPCR_CURRENT_THREAD]
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Raise to synch level */
|
|
call _KeRaiseIrqlToSynchLevel@0
|
|
|
|
/* Set context swap busy */
|
|
mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
|
|
|
|
/* Acquire the PRCB Lock */
|
|
lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
|
|
jnb GetNext
|
|
lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
|
|
call @KefAcquireSpinLockAtDpcLevel@4
|
|
#endif
|
|
|
|
GetNext:
|
|
/* Get the next thread and clear it */
|
|
mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
|
|
and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
|
|
|
|
/* Set us as the current running thread */
|
|
mov [ebx+KPCR_CURRENT_THREAD], esi
|
|
mov byte ptr [esi+KTHREAD_STATE_], Running
|
|
mov byte ptr [edi+KTHREAD_WAIT_REASON], WrDispatchInt
|
|
|
|
/* Put thread in ECX and get the PRCB in EDX */
|
|
mov ecx, edi
|
|
lea edx, [ebx+KPCR_PRCB_DATA]
|
|
call @KiQueueReadyThread@8
|
|
|
|
/* Set APC_LEVEL and do the swap */
|
|
mov cl, APC_LEVEL
|
|
call @KiSwapContextInternal@0
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Lower IRQL back to dispatch */
|
|
mov cl, DISPATCH_LEVEL
|
|
call @KfLowerIrql@4
|
|
#endif
|
|
|
|
/* Restore registers */
|
|
mov ebp, [esp+0]
|
|
mov edi, [esp+4]
|
|
mov esi, [esp+8]
|
|
add esp, 3*4
|
|
|
|
Return:
|
|
/* All done */
|
|
pop ebx
|
|
ret
|
|
|
|
QuantumEnd:
|
|
/* Disable quantum end and process it */
|
|
mov byte ptr [ebx+KPCR_PRCB_QUANTUM_END], 0
|
|
call _KiQuantumEnd@0
|
|
pop ebx
|
|
ret
|
|
.endfunc
|
|
|
|
.globl @KiIdleLoop@0
|
|
.func @KiIdleLoop@0, @KiIdleLoop@0
|
|
@KiIdleLoop@0:
|
|
|
|
/* Set EBX */
|
|
mov ebx, fs:[KPCR_SELF]
|
|
|
|
/* Jump into mainline code */
|
|
jmp MainLoop
|
|
|
|
CpuIdle:
|
|
/* Call the CPU's idle function */
|
|
lea ecx, [ebx+KPCR_PRCB_POWER_STATE_IDLE_FUNCTION]
|
|
call [ecx]
|
|
|
|
MainLoop:
|
|
/* Cycle interrupts for 1 cycle */
|
|
sti
|
|
nop
|
|
nop
|
|
cli
|
|
|
|
/* Check if we have to deliver DPCs, timers, or deferred threads */
|
|
mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
|
|
or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
|
|
#ifdef CONFIG_SMP
|
|
or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
|
|
#endif
|
|
jz CheckSchedule
|
|
|
|
mov cl, DISPATCH_LEVEL
|
|
call @HalClearSoftwareInterrupt@4
|
|
|
|
/* Handle the above */
|
|
lea ecx, [ebx+KPCR_PRCB_DATA]
|
|
call @KiRetireDpcList@4
|
|
|
|
CheckSchedule:
|
|
/* Check if a next thread is queued */
|
|
cmp dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
|
|
#ifdef CONFIG_SMP
|
|
jz NoNextThread
|
|
#else
|
|
jz CpuIdle
|
|
#endif
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* There is, raise IRQL to synch level */
|
|
call _KeRaiseIrqlToSynchLevel@0
|
|
#endif
|
|
sti
|
|
|
|
/* Set the current thread to ready */
|
|
mov edi, [ebx+KPCR_CURRENT_THREAD]
|
|
#ifdef CONFIG_SMP
|
|
mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
|
|
|
|
/* Acquire the PRCB Lock */
|
|
lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
|
|
jnb CheckNext
|
|
lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
|
|
call @KefAcquireSpinLockAtDpcLevel@4
|
|
#endif
|
|
|
|
CheckNext:
|
|
/* Check if the next thread is the current */
|
|
mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
|
|
#ifdef CONFIG_SMP
|
|
cmp esi, edi
|
|
jz SameThread
|
|
#endif
|
|
|
|
/* Clear the next thread and set this one instead */
|
|
and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
|
|
mov [ebx+KPCR_CURRENT_THREAD], esi
|
|
|
|
/* Set the thread as running */
|
|
mov byte ptr [esi+KTHREAD_STATE_], Running
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Disable the idle scheduler and release the PRCB lock */
|
|
and byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
|
|
and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
|
|
#endif
|
|
|
|
SwapContext:
|
|
/* ReactOS Mm Hack */
|
|
mov ecx, esi
|
|
call @MiSyncForContextSwitch@4
|
|
|
|
/* Swap context at APC_LEVEL */
|
|
mov ecx, APC_LEVEL
|
|
call @KiSwapContextInternal@0
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Lower to DPC level */
|
|
mov ecx, DISPATCH_LEVEL
|
|
call @KfLowerIrql@4
|
|
#endif
|
|
jmp MainLoop
|
|
|
|
#ifdef CONFIG_SMP
|
|
SameThread:
|
|
/* Clear the next thread, and put the thread as ready after lock release */
|
|
and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
|
|
and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
|
|
and byte ptr [edi+KTHREAD_STATE_], Ready
|
|
jmp MainLoop
|
|
|
|
NoNextThread:
|
|
/* Check if the idle scheduler is enabled */
|
|
cmp byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
|
|
jz CpuIdle
|
|
|
|
/* It is, so call the scheduler */
|
|
lea ecx, [ebx+KPCR_PRCB_DATA]
|
|
call @KiIdleSchedule@4
|
|
test eax, eax
|
|
|
|
/* Get new thread pointers and either swap or idle loop again */
|
|
mov esi, eax
|
|
mov edi, [ebx+KPCR_PRCB_IDLE_THREAD]
|
|
jnz SwapContext
|
|
jmp MainLoop
|
|
#endif
|
|
.endfunc
|
|
|
|
/* FIXFIX: Move to C code ****/
|
|
.globl _Ki386SetupAndExitToV86Mode@4
|
|
.func Ki386SetupAndExitToV86Mode@4
|
|
_Ki386SetupAndExitToV86Mode@4:
|
|
|
|
/* Enter V8086 mode */
|
|
pushad
|
|
sub esp, (12 + KTRAP_FRAME_LENGTH + NPX_FRAME_LENGTH)
|
|
mov ecx, esp
|
|
call @KiEnterV86Mode@4
|
|
jmp $
|
|
.endfunc
|
|
|
|
.globl @Ki386BiosCallReturnAddress@4
|
|
@Ki386BiosCallReturnAddress@4:
|
|
|
|
/* Exit V8086 mode */
|
|
call @KiExitV86Mode@4
|
|
mov esp, eax
|
|
add esp, (12 + KTRAP_FRAME_LENGTH + NPX_FRAME_LENGTH)
|
|
popad
|
|
ret 4
|
|
|