/* INCLUDES ******************************************************************/
-#include <ndk/asm.h>
-.intel_syntax noprefix
+#include <asm.inc>
+#include <ks386.inc>
-#define Ready 1
-#define Running 2
-#define WrDispatchInt 0x1F
+EXTERN @KiSwapContextEntry@8:PROC
+EXTERN @KiSwapContextExit@8:PROC
+EXTERN @KiRetireDpcList@4:PROC
+EXTERN @KiEnterV86Mode@4:PROC
+EXTERN @KiExitV86Mode@4:PROC
/* FUNCTIONS ****************************************************************/
+.code
-/*++
- * 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
+PUBLIC @KiSwapContextInternal@0
@KiSwapContextInternal@0:
+ /* Build switch frame */
+ sub esp, 2 * 4
+ mov ecx, esp
+ jmp @KiSwapContextEntry@8
- /* 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
+PUBLIC @KiSwapContext@8
@KiSwapContext@8:
-
/* Save 4 registers */
sub esp, 4 * 4
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]
+ or dl, cl
/* Do the swap with the registers correctly setup */
call @KiSwapContextInternal@0
/* 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
+PUBLIC @KiSwitchThreads@8
+@KiSwitchThreads@8:
+ /* Load the new kernel stack and switch OS to new thread */
+ mov esp, edx
+ call @KiSwapContextExit@8
+
+ /* Now we're on the new thread. Return to the caller to restore registers */
+ add esp, 2 * 4
+ ret
- /* 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]
+PUBLIC @KiRetireDpcListInDpcStack@8
+@KiRetireDpcListInDpcStack@8:
+ /* Switch stacks and retire DPCs */
+ mov eax, esp
+ mov esp, edx
+ push eax
call @KiRetireDpcList@4
- /* Restore stack and exception list */
+ /* Return on original stack */
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
+ ret
- /* 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
+PUBLIC _Ki386EnableCurrentLargePage@8
+_Ki386EnableCurrentLargePage@8:
+ /* Save StartAddress in eax */
+ mov eax, [esp + 4]
- /* Put thread in ECX and get the PRCB in EDX */
- mov ecx, edi
- lea edx, [ebx+KPCR_PRCB_DATA]
- call @KiQueueReadyThread@8
+ /* Save new CR3 value in ecx */
+ mov ecx, [esp + 8]
- /* Set APC_LEVEL and do the swap */
- mov cl, APC_LEVEL
- call @KiSwapContextInternal@0
+ /* Save flags value */
+ pushfd
-#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
+ /* Disable interrupts */
+ cli
-Return:
- /* All done */
- pop ebx
- ret
+ /* Compute linear address */
+ sub eax, offset _Ki386EnableCurrentLargePage@8
+ add eax, offset _Ki386LargePageIdentityLabel
-QuantumEnd:
- /* Disable quantum end and process it */
- mov byte ptr [ebx+KPCR_PRCB_QUANTUM_END], 0
- call _KiQuantumEnd@0
- pop ebx
- ret
-.endfunc
+ /* Save old CR3 in edx and replace with a new one */
+ mov edx, cr3
+ mov cr3, ecx
-.globl @KiIdleLoop@0
-.func @KiIdleLoop@0, @KiIdleLoop@0
-@KiIdleLoop@0:
+ /* Jump to the next instruction but in linear mapping */
+ jmp eax
- /* Set EBX */
- mov ebx, fs:[KPCR_SELF]
+_Ki386LargePageIdentityLabel:
+ /* Disable paging */
+ mov eax, cr0
+ and eax, NOT CR0_PG
+ mov cr0, eax
- /* Jump into mainline code */
- jmp MainLoop
+ /* Jump to the next instruction to clear the prefetch queue */
+ jmp $+2
-CpuIdle:
- /* Call the CPU's idle function */
- lea ecx, [ebx+KPCR_PRCB_POWER_STATE_IDLE_FUNCTION]
- call [ecx]
+ /* Enable Page Size Extension in CR4 */
+ mov ecx, cr4
+ or ecx, CR4_PSE
+ mov cr4, ecx
-MainLoop:
- /* Cycle interrupts for 1 cycle */
- sti
- nop
- nop
- cli
+ /* Done, now re-enable paging */
+ or eax, CR0_PG
+ mov cr0, eax
- /* 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
+ /* Jump to virtual address */
+ mov eax, offset VirtualSpace
+ jmp eax
- mov cl, DISPATCH_LEVEL
- call @HalClearSoftwareInterrupt@4
-
- /* Handle the above */
- lea ecx, [ebx+KPCR_PRCB_DATA]
- call @KiRetireDpcList@4
+VirtualSpace:
+ /* Restore CR3 contents */
+ mov cr3, edx
-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
+ /* Restore flags */
+ popfd
-#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
+ ret 8
/* FIXFIX: Move to C code ****/
-.globl _Ki386SetupAndExitToV86Mode@4
-.func Ki386SetupAndExitToV86Mode@4
+PUBLIC _Ki386SetupAndExitToV86Mode@4
_Ki386SetupAndExitToV86Mode@4:
/* Enter V8086 mode */
mov ecx, esp
call @KiEnterV86Mode@4
jmp $
-.endfunc
-.globl @Ki386BiosCallReturnAddress@4
+
+PUBLIC @Ki386BiosCallReturnAddress@4
@Ki386BiosCallReturnAddress@4:
/* Exit V8086 mode */
popad
ret 4
+END