Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1150 lines
34 KiB

title "System Startup"
;++
;
; Copyright (c) 1989 Microsoft Corporation
;
; Module Name:
;
; systembg.asm
;
; Abstract:
;
; This module implements the code necessary to initially startup the
; NT system.
;
; Author:
;
; Shie-Lin Tzong (shielint) 07-Mar-1990
;
; Environment:
;
; Kernel mode only.
;
; Revision History:
;
; John Vert (jvert) 25-Jun-1991
; Major overhaul in order to move into new osloader architecture
; Removed old debugger hacks
;
;--
.386p
.xlist
include i386\cpu.inc
include ks386.inc
include i386\kimacro.inc
include mac386.inc
include callconv.inc
.list
option segment:flat
extrn @ExfInterlockedPopEntrySList@8:DWORD
extrn @ExfInterlockedPushEntrySList@12:DWORD
extrn @ExInterlockedCompareExchange64@16:DWORD
extrn @ExInterlockedPopEntrySList@8:DWORD
extrn @ExInterlockedPushEntrySList@12:DWORD
extrn @ExpInterlockedCompareExchange64@16:DWORD
extrn _ExInterlockedAddLargeInteger@16:DWORD
extrn _ExInterlockedExchangeAddLargeInteger@16:DWORD
extrn _KiBootFeatureBits:DWORD
EXTRNP _KdInitSystem,2
EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
EXTRNP _KiInitializeKernel,6
extrn SwapContext:PROC
EXTRNP GetMachineBootPointers
EXTRNP _KiInitializePcr,6
EXTRNP _KiSwapIDT
EXTRNP _KiInitializeTSS,1
EXTRNP _KiInitializeTSS2,2
EXTRNP _KiInitializeGdtEntry,6
extrn _KiTrap08:PROC
extrn _KiTrap02:PROC
EXTRNP _HalDisplayString,1,IMPORT
EXTRNP _KiInitializeAbios,1
EXTRNP _KiInitializeMachineType
EXTRNP _KeGetCurrentIrql,0,IMPORT
EXTRNP _KeBugCheck, 1
EXTRNP _KeBugCheckEx, 5
EXTRNP _HalInitializeProcessor,1,IMPORT
EXTRNP _HalProcessorIdle,0,IMPORT
EXTRNP HalClearSoftwareInterrupt,1,IMPORT,FASTCALL
EXTRNP _ZwAcceptConnectPort,6
EXTRNP _ZwUnmapViewOfSection,2
if NT_INST
EXTRNP _KiAcquireSpinLock, 1
EXTRNP _KiReleaseSpinLock, 1
endif
extrn _KiFreezeExecutionLock:DWORD
extrn _KiDispatcherLock:DWORD
extrn _IDT:BYTE
extrn _IDTLEN:BYTE ; NOTE - really an ABS, linker problems
extrn _KeNumberProcessors:BYTE
extrn _KeActiveProcessors:DWORD
extrn _KiIdleSummary:DWORD
extrn _KiProcessorBlock:DWORD
extrn _KiFindFirstSetRight:BYTE
EXTRNP _KdPollBreakIn,0
extrn _KeLoaderBlock:DWORD
extrn _KeI386NpxPresent:DWORD
extrn _KeI386CpuType:DWORD
extrn _KeI386CpuStep:DWORD
extrn _KeTickCount:DWORD
ifndef NT_UP
extrn _KiBarrierWait:DWORD
endif
if DBG
extrn _KdDebuggerEnabled:BYTE
EXTRNP _DbgBreakPoint,0
extrn _DbgPrint:near
extrn _KiDPCTimeout:DWORD
extrn _MsgDpcTrashedEsp:BYTE
extrn _MsgDpcTimeout:BYTE
endif
;
; Constants for various variables
;
_DATA SEGMENT PARA PUBLIC 'DATA'
;
; Idle thread process object
;
align 4
public _KiIdleProcess
_KiIdleProcess label byte
db ExtendedProcessObjectLength dup(?) ; sizeof (EPROCESS)
;
; Staticly allocated structures for Bootstrap processor
; idle thread object for P0
; idle thread stack for P0
;
align 4
public P0BootThread
P0BootThread label byte
db ExtendedThreadObjectLength dup(?) ; sizeof (ETHREAD)
;
; I don't think it is safe to overlap P0 stack and NMI/DoubleFault stack.
; The NMI handler may decide to continue. We need to make sure the original
; stack content is complete.
; [shielint]
;
align 16
if DBG
public _KiDoubleFaultStack
db DOUBLE_FAULT_STACK_SIZE dup (?)
_KiDoubleFaultStack label byte
endif
public P0BootStack
db KERNEL_STACK_SIZE dup (?)
P0BootStack label byte
;
; Double fault task stack
;
MINIMUM_TSS_SIZE EQU TssIoMaps
align 16
public _KiDoubleFaultTSS
_KiDoubleFaultTSS label byte
db MINIMUM_TSS_SIZE dup(0)
public _KiNMITSS
_KiNMITSS label byte
db MINIMUM_TSS_SIZE dup(0)
;
; Abios specific definitions
;
public _KiCommonDataArea, _KiAbiosPresent
_KiCommonDataArea dd 0
_KiAbiosPresent dd 0
_DATA ends
page ,132
subttl "System Startup"
INIT SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
DFInternalError db 'DF Stack internal error', 0
;++
;
; For processor 0, Routine Description:
;
; This routine is called when the NT system begins execution.
; Its function is to initialize system hardware state, call the
; kernel initialization routine, and then fall into code that
; represents the idle thread for all processors.
;
; Entry state created by the boot loader:
; A short-form IDT (0-1f) exists and is active.
; A complete GDT is set up and loaded.
; A complete TSS is set up and loaded.
; Page map is set up with minimal start pages loaded
; The lower 4Mb of virtual memory are directly mapped into
; physical memory.
;
; The system code (ntoskrnl.exe) is mapped into virtual memory
; as described by its memory descriptor.
; DS=ES=SS = flat
; ESP->a useable boot stack
; Interrupts OFF
;
; For processor > 0, Routine Description:
;
; This routine is called when each additional processor begins execution.
; The entry state for the processor is:
; IDT, GDT, TSS, stack, selectors, PCR = all valid
; Page directory is set to the current running direcroty
; LoaderBlock - parameters for this processors
;
; Arguments:
;
; PLOADER_PARAMETER_BLOCK LoaderBlock
;
; Return Value:
;
; None.
;
;--
;
; Arguments for KiSystemStartupPx
;
KissLoaderBlock equ [ebp+8]
;
; Local variables
;
KissGdt equ [ebp-4]
KissPcr equ [ebp-8]
KissTss equ [ebp-12]
KissIdt equ [ebp-16]
KissIrql equ [ebp-20]
KissPbNumber equ [ebp-24]
KissIdleStack equ [ebp-28]
KissIdleThread equ [ebp-32]
cPublicProc _KiSystemStartup ,1
push ebp
mov ebp, esp
sub esp, 32 ; Reserve space for local variables
mov ebx, dword ptr KissLoaderBlock
mov _KeLoaderBlock, ebx ; Get loader block param
movzx ecx, _KeNumberProcessors ; get number of processors
mov KissPbNumber, ecx
or ecx, ecx ; Is the the boot processor?
jnz @f ; no
; P0 uses static memory for these
mov dword ptr [ebx].LpbThread, offset P0BootThread
mov dword ptr [ebx].LpbKernelStack, offset P0BootStack
push KGDT_R0_PCR ; P0 needs FS set
pop fs
; Save processornumber in Prcb
mov byte ptr fs:PcPrcbData+PbNumber, cl
@@:
mov eax, dword ptr [ebx].LpbThread
mov dword ptr KissIdleThread, eax
mov eax, dword ptr [ebx].LpbKernelStack
mov dword ptr KissIdleStack, eax
stdCall _KiInitializeMachineType
cmp byte ptr KissPbNumber, 0 ; if not p0, then
jne kiss_notp0 ; skip a bunch
;
;+++++++++++++++++++++++
;
; Initialize the PCR
;
stdCall GetMachineBootPointers
;
; Upon return:
; (edi) -> gdt
; (esi) -> pcr
; (edx) -> tss
; (eax) -> idt
; Now, save them in our local variables
;
mov KissGdt, edi
mov KissPcr, esi
mov KissTss, edx
mov KissIdt, eax
;
; edit TSS to be 32bits. loader gives us a tss, but it's 16bits!
;
lea ecx,[edi]+KGDT_TSS ; (ecx) -> TSS descriptor
mov byte ptr [ecx+5],089h ; 32bit, dpl=0, present, TSS32, not busy
; KiInitializeTSS2(
; Linear address of TSS
; Linear address of TSS descriptor
; );
stdCall _KiInitializeTSS2, <KissTss, ecx>
stdCall _KiInitializeTSS, <KissTss>
mov cx,KGDT_TSS
ltr cx
;
; set up 32bit double fault task gate so we can catch double faults.
;
mov eax,KissIdt
lea ecx,[eax]+40h ; Descriptor 8
mov byte ptr [ecx+5],085h ; dpl=0, present, taskgate
mov word ptr [ecx+2],KGDT_DF_TSS
lea ecx,[edi]+KGDT_DF_TSS
mov byte ptr [ecx+5],089h ; 32bit, dpl=0, present, TSS32, not busy
mov edx,offset FLAT:_KiDoubleFaultTSS
mov eax,edx
mov [ecx+KgdtBaseLow],ax
shr eax,16
mov [ecx+KgdtBaseHi],ah
mov [ecx+KgdtBaseMid],al
mov eax, MINIMUM_TSS_SIZE
mov [ecx+KgdtLimitLow],ax
; KiInitializeTSS(
; address of double fault TSS
; );
stdCall _KiInitializeTSS, <edx>
mov eax,cr3
mov [edx+TssCr3],eax
if DBG
mov eax, offset FLAT:_KiDoubleFaultStack
else
; on a retail build we overload the double fault stack to overlay
; part of the kernel's image. (we overlay the ZW thunks)
mov eax, offset FLAT:_ZwUnmapViewOfSection@8 - 4
and eax, not 3
push eax
sub eax, offset FLAT:_ZwAcceptConnectPort@24
cmp eax, 0a00h ; make sure there's enough stack
jnc short @f ; space available
pushad
stdCall _HalDisplayString, <offset FLAT:DFInternalError>
popad
@@:
pop eax
endif
mov dword ptr [edx+038h],eax
mov dword ptr [edx+TssEsp0],eax
mov dword ptr [edx+020h],offset FLAT:_KiTrap08
mov dword ptr [edx+024h],0 ; eflags
mov word ptr [edx+04ch],KGDT_R0_CODE ; set value for CS
mov word ptr [edx+058h],KGDT_R0_PCR ; set value for FS
mov [edx+050h],ss
mov word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es
mov word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds
;
; set up 32bit NMI task gate so we can catch NMI faults.
;
mov eax,KissIdt
lea ecx,[eax]+10h ; Descriptor 2
mov byte ptr [ecx+5],085h ; dpl=0, present, taskgate
mov word ptr [ecx+2],KGDT_NMI_TSS
lea ecx,[edi]+KGDT_NMI_TSS
mov byte ptr [ecx+5],089h ; 32bit, dpl=0, present, TSS32, not busy
mov edx,offset FLAT:_KiNMITSS
mov eax,edx
mov [ecx+KgdtBaseLow],ax
shr eax,16
mov [ecx+KgdtBaseHi],ah
mov [ecx+KgdtBaseMid],al
mov eax, MINIMUM_TSS_SIZE
mov [ecx+KgdtLimitLow],ax
push edx
stdCall _KiInitializeTSS,<edx> ; KiInitializeTSS(
; address TSS
; );
; We are using the DoubleFault stack as the DoubleFault stack and the
; NMI Task Gate stack
mov eax,cr3
mov [edx+TssCr3],eax
mov eax, offset FLAT:_KiDoubleFaultTSS
mov eax, dword ptr [eax+038h] ; get DF stack
mov dword ptr [edx+TssEsp0],eax ; use it for NMI stack
mov dword ptr [edx+038h],eax
mov dword ptr [edx+020h],offset FLAT:_KiTrap02
mov dword ptr [edx+024h],0 ; eflags
mov word ptr [edx+04ch],KGDT_R0_CODE ; set value for CS
mov word ptr [edx+058h],KGDT_R0_PCR ; set value for FS
mov [edx+050h],ss
mov word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es
mov word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds
stdCall _KiInitializePcr, <KissPbNumber,KissPcr,KissIdt,KissGdt,KissTss,KissIdleThread>
;
; set current process pointer in current thread object
;
mov edx, KissIdleThread
mov ecx, offset FLAT:_KiIdleProcess ; (ecx)-> idle process obj
mov [edx]+ThApcState+AsProcess, ecx ; set addr of thread's process
;
; set up PCR: Teb, Prcb pointers. The PCR:InitialStack, and various fields
; of Prcb will be set up in _KiInitializeKernel
;
mov dword ptr fs:PcTeb, 0 ; PCR->Teb = 0
;
; Initialize KernelDr7 and KernelDr6 to 0. This must be done before
; the debugger is called.
;
mov dword ptr fs:PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr6,0
mov dword ptr fs:PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr7,0
;
; Since the entries of Kernel IDT have their Selector and Extended Offset
; fields set up in the wrong order, we need to swap them back to the order
; which i386 recognizes.
; This is only done by the bootup processor.
;
stdCall _KiSwapIDT ; otherwise, do the work
;
; Switch to R3 flat selectors that we want loaded so lazy segment
; loading will work.
;
mov eax,KGDT_R3_DATA OR RPL_MASK ; Set RPL = ring 3
mov ds,ax
mov es,ax
;
; Now copy our trap handlers to replace kernel debugger's handlers.
;
mov eax, KissIdt ; (eax)-> Idt
push dword ptr [eax+40h] ; save double fault's descriptor
push dword ptr [eax+44h]
push dword ptr [eax+10h] ; save nmi fault's descriptor
push dword ptr [eax+14h]
mov edi,KissIdt
mov esi,offset FLAT:_IDT
mov ecx,offset FLAT:_IDTLEN ; _IDTLEN is really an abs, we use
shr ecx,2
rep movsd
pop dword ptr [eax+14h] ; restore nmi fault's descriptor
pop dword ptr [eax+10h]
pop dword ptr [eax+44h] ; restore double fault's descriptor
pop dword ptr [eax+40h]
kiss_notp0:
;
; A new processor can't come online while execution is frozen
; Take freezelock while adding a processor to the system
; NOTE: don't use SPINLOCK macro - it has debugger stuff in it
;
if NT_INST
lea eax, _KiFreezeExecutionLock
stdCall _KiAcquireSpinLock, <eax>
else
@@: test _KiFreezeExecutionLock, 1
jnz short @b
lock bts _KiFreezeExecutionLock, 0
jc short @b
endif
;
; Add processor to active summary, and update BroadcastMasks
;
mov ecx, dword ptr KissPbNumber ; mark this processor as active
mov byte ptr fs:PcNumber, cl
mov eax, 1
shl eax, cl ; our affinity bit
or _KeActiveProcessors, eax ; New affinity of active processors
mov fs:PcSetMember, eax
mov fs:PcPrcbData.PbSetMember, eax
;
; Initialize the interprocessor interrupt vector and increment ready
; processor count to enable kernel debugger.
;
stdCall _HalInitializeProcessor, <dword ptr KissPbNumber>
;
; Initialize ABIOS data structure if present.
; Note, the KiInitializeAbios MUST be called after the KeLoaderBlock is
; initialized.
;
stdCall _KiInitializeAbios, <dword ptr KissPbNumber>
inc _KeNumberProcessors ; One more processor now active
if NT_INST
lea eax, _KiFreezeExecutionLock
stdCall _KiReleaseSpinLock, <eax>
else
xor eax, eax ; release the executionlock
mov _KiFreezeExecutionLock, eax
endif
cmp byte ptr KissPbNumber, 0
jnz @f
; don't stop in debugger
stdCall _KdInitSystem, <_KeLoaderBlock,0>
if DEVL
;
; Give debugger an opportunity to gain control.
;
POLL_DEBUGGER
endif ; DEVL
@@:
nop ; leave a spot for int-3 patch
;
; Set initial IRQL = HIGH_LEVEL for init
;
mov ecx, HIGH_LEVEL
fstCall KfRaiseIrql
mov KissIrql, al
;
; If the target machine does not implement the cmpxchg8b instruction,
; then patch the routines that use this instrucntion to simply jump
; to the corresponding routines that use spinlocks.
;
pushfd ; Save flags
cmp byte ptr KissPbNumber, 0
jnz cx8done ; only test on boot processor
pop ebx ; Get flags into eax
push ebx ; Save original flags
mov ecx, ebx
xor ecx, EFLAGS_ID ; flip ID bit
push ecx
popfd ; load it into flags
pushfd ; re-save flags
pop ecx ; get flags into eax
cmp ebx, ecx ; did bit stay flipped?
je short nocx8 ; No, don't try CPUID
or ebx, EFLAGS_ID
push ebx
popfd ; Make sure ID bit is set
.586p
mov eax, 1 ; Get feature bits
cpuid ; Uses eax, ebx, ecx, edx
.386p
test edx, 100h
jz short nocx8
or _KiBootFeatureBits, KF_CMPXCHG8B ; We're committed to using
jmp short cx8done ; this feature
nocx8:
lea eax, @ExInterlockedCompareExchange64@16 ; get target address
lea ecx, @ExpInterlockedCompareExchange64@16 ; get source address
mov byte ptr [eax], 0e9H ; set jump opcode value
lea edx, [eax] + 5 ; get simulated eip value
sub ecx, edx ; compute displacement
mov [eax] + 1, ecx ; set jump displacement value
lea eax, @ExInterlockedPopEntrySList@8 ; get target address
lea ecx, @ExfInterlockedPopEntrySList@8 ; get source address
mov byte ptr [eax], 0e9H ; set jump opcode value
lea edx, [eax] + 5 ; get simulated eip value
sub ecx, edx ; compute displacement
mov [eax] + 1, ecx ; set jump displacement value
lea eax, @ExInterlockedPushEntrySList@12 ; get target address
lea ecx, @ExfInterlockedPushEntrySList@12 ; get source address
mov byte ptr [eax], 0e9H ; set jump opcode value
lea edx, [eax] + 5 ; get simulated eip value
sub ecx, edx ; compute displacement
mov [eax] + 1, ecx ; set jump displacement value
lea eax, _ExInterlockedExchangeAddLargeInteger@16 ; get target address
lea ecx, _ExInterlockedAddLargeInteger@16 ; get source address
mov byte ptr [eax], 0e9H ; set jump opcode value
lea edx, [eax] + 5 ; get simulated eip value
sub ecx, edx ; compute displacement
mov [eax] + 1, ecx ; set jump displacement value
cx8done:
popfd
;
; Initialize ebp, esp, and argument registers for initializing the kernel.
;
mov ebx, KissIdleThread
mov edx, KissIdleStack
mov eax, KissPbNumber
and edx, NOT 3h ; align stack to 4 byte boundary
xor ebp, ebp ; (ebp) = 0. No more stack frame
mov esp, edx
push CR0_EM+CR0_TS+CR0_MP ; make space for Cr0NpxState
; arg6 - LoaderBlock
; arg5 - processor number
; arg4 - addr of prcb
; arg3 - idle thread's stack
; arg2 - addr of current thread obj
; arg1 - addr of current process obj
; initialize system data structures
; and HAL.
stdCall _KiInitializeKernel,<offset _KiIdleProcess,ebx,edx,dword ptr fs:PcPrcb,eax,_KeLoaderBlock>
;
; Set "shadow" priority value for Idle thread. This will keep the Mutex
; priority boost/drop code from dropping priority on the Idle thread, and
; thus avoids leaving a bit set in the ActiveMatrix for the Idle thread when
; there should never be any such bit.
;
mov ebx,fs:PcPrcbData+PbCurrentThread ; (eax)->Thread
mov byte ptr [ebx]+ThPriority,LOW_REALTIME_PRIORITY ; set pri.
;
; Control is returned to the idle thread with IRQL at HIGH_LEVEL. Lower IRQL
; to DISPATCH_LEVEL and set wait IRQL of idle thread.
;
sti
mov ecx, DISPATCH_LEVEL
fstCall KfLowerIrql
mov byte ptr [ebx]+ThWaitIrql, DISPATCH_LEVEL
;
; The following code represents the idle thread for a processor. The idle
; thread executes at IRQL DISPATCH_LEVEL and continually polls for work to
; do. Control may be given to this loop either as a result of a return from
; the system initialize routine or as the result of starting up another
; processor in a multiprocessor configuration.
;
mov ebx, PCR[PcSelfPcr] ; get address of PCR
;
; In a multiprocessor system the boot processor proceeds directly into
; the idle loop. As other processors start executing, however, they do
; not directly enter the idle loop and spin until all processors have
; been started and the boot master allows them to proceed.
;
ifndef NT_UP
@@: cmp _KiBarrierWait, 0 ; check if barrier set
jnz short @b ; if nz, barrier set
endif
jmp KiIdleLoop ; enter idle loop
stdENDP _KiSystemStartup
INIT ends
_TEXT$00 SEGMENT DWORD PUBLIC 'CODE' ; Put IdleLoop in text section
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Idle Loop"
;++
;
; Routine Description:
;
; This routine continuously executes the idle loop and never returns.
;
; Arguments:
;
; ebx - Address of the current processor PCR.
;
; Return value:
;
; None - routine never returns.
;
;--
public KiIdleLoop
KiIdleLoop proc
lea ebp, [ebx].PcPrcbData.PbDpcListHead ; set DPC listhead address
if DBG
xor edi, edi ; reset poll breakin counter
endif
jmp short kid20 ; Skip HalIdleProcessor on first iteration
;
; There are no entries in the DPC list and a thread has not been selected
; for execution on this processor. Call the HAL so power managment can be
; performed.
;
; N.B. The HAL is called with interrupts disabled. The HAL will return
; with interrupts enabled.
;
; N.B. Use a call instruction instead of a push-jmp, as the call instruction
; executes faster and won't invalidate the processors call-return stack
; cache.
;
kid10: stdCall _HalProcessorIdle ;
;
; Give debugger an opportunity to gain control on debug systems.
;
; N.B. On an MP system the lowest numbered idle processor is the only
; processor that polls for a breakin request.
;
kid20:
if DBG
ifndef NT_UP
mov eax, _KiIdleSummary ; get idle summary
mov ecx, [ebx].PcSetMember ; get set member
dec ecx ; compute right bit mask
and eax, ecx ; check if any lower bits set
jnz short CheckDpcList ; if nz, not lowest numbered
endif
dec edi ; decrement poll counter
jg short CheckDpcList ; if g, not time to poll
POLL_DEBUGGER ; check if break in requested
endif
kid30:
if DBG
ifndef NT_UP
mov edi, 20 * 1000 ; set breakin poll interval
else
mov edi, 100 ; UP idle loop has a HLT in it
endif
endif
;
; Disable interrupts and check if there is any work in the DPC list
; of the current processor or a target processor.
;
CheckDpcList: ;
;
; N.B. The following code enables interrupts for a few cycles, then
; disables them again for the subsequent DPC and next thread
; checks.
;
sti ; enable interrupts
nop ;
nop ;
cli ; disable interrupts
;
; Process the deferred procedure call list for the current processor.
;
cmp ebp, [ebp].LsFlink ; check if DPC list is empty
je short CheckNextThread ; if eq, DPC list is empty
mov cl, DISPATCH_LEVEL ; set interrupt level
fstCall HalClearSoftwareInterrupt ; clear software interrupt
call KiRetireDpcList ; process the current DPC list
if DBG
xor edi, edi ; clear breakin poll interval
endif
;
; Check if a thread has been selected to run on the current processor.
;
CheckNextThread: ;
cmp dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; thread selected?
je short kid10 ; if eq, no thread selected
;
; A thread has been selected for execution on this processor. Acquire
; dispatcher database lock, get the thread address again (it may have
; changed), clear the address of the next thread in the processor block,
; and call swap context to start execution of the selected thread.
;
; N.B. If the dispatcher database lock cannot be obtained immediately,
; then attempt to process another DPC rather than spinning on the
; dispatcher database lock.
;
; N.B. This polls for the spinlock by first using a non-interlocked
; instruction. This way if the lock is busy, and the code is
; spinning, this processor won't be generating lots of locked cycles
;
ifndef NT_UP
lea eax, _KiDispatcherLock ; get address of dispatcher lock
TEST_SPINLOCK eax, <short CheckDpcList>
ACQUIRE_SPINLOCK eax, <short CheckDpcList>, NoChecking ; acquire dispatcher database lock
endif
;
; Raise IRQL to synchronization level and enable interrupts.
;
mov ecx, SYNCH_LEVEL ; raise IRQL to synchronization level
fstCall KfRaiseIrql ;
sti ; enable interrupts
mov esi, [ebx].PcPrcbData.PbNextThread ; get next thread address
mov edi, [ebx].PcPrcbData.PbCurrentThread ; set current thread address
mov dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread address
mov [ebx].PcPrcbData.PbCurrentThread, esi ; set current thread address
mov cl, 1 ; set APC interrupt bypass disable
call SwapContext ;
mov ecx, DISPATCH_LEVEL ; lower IRQL to dispatch level
fstCall KfLowerIrql ;
lea ebp, [ebx].PcPrcbData.PbDpcListHead ; set DPC listhead address
jmp kid30 ;
KiIdleLoop endp
page ,132
subttl "Retire Deferred Procedure Call List"
;++
;
; Routine Description:
;
; This routine is called to retire the specified deferred procedure
; call list. DPC routines are called using the idle thread (current)
; stack.
;
; N.B. Interrupts must be disabled and the DPC list lock held on entry
; to this routine. Control is returned to the caller with the same
; conditions true.
;
; N.B. The registers ebx and ebp are preserved across the call.
;
; Arguments:
;
; ebx - Address of the target processor PCR.
; ebp - Address of the target DPC listhead.
;
; Return value:
;
; None.
;
;--
public KiRetireDpcList
KiRetireDpcList proc
ifndef NT_UP
push esi ; save register
lea esi, [ebx].PcPrcbData.PbDpcLock ; get DPC lock address
endif
rdl5: mov PCR[PcPrcbData.PbDpcRoutineActive], esp ; set DPC routine active
;
; Process the DPC List.
;
rdl10: ;
ifndef NT_UP
ACQUIRE_SPINLOCK esi, rdl50, NoChecking ; acquire DPC lock
cmp ebp, [ebp].LsFlink ; check if DPC list is empty
je rdl45 ; if eq, DPC list is empty
endif
mov edx, [ebp].LsFlink ; get address of next entry
mov ecx, [edx].LsFlink ; get address of next entry
mov [ebp].LsFlink, ecx ; set address of next in header
mov [ecx].LsBlink, ebp ; set address of previous in next
sub edx, DpDpcListEntry ; compute address od DPC object
mov ecx, [edx].DpDeferredRoutine ; get DPC routine address
if DBG
.fpo (5, 0, 0, 0, 0, 0)
push edi ; save register
push ecx ; save DPC routine address
push dword ptr PCR[PcPrcbData.PbInterruptCount] ; save interrupt count
push dword ptr PCR[PcPrcbData.PbInterruptTime] ; save interrupt time
push _KeTickCount ; save current tick count
mov edi, esp ; save current stack pointer
endif
.fpo (4, 0, 0, 0, 0, 0)
push [edx].DpSystemArgument2 ; second system argument
push [edx].DpSystemArgument1 ; first system argument
push [edx].DpDeferredContext ; get deferred context argument
push edx ; address of DPC object
mov dword ptr [edx]+DpLock, 0 ; clear DPC inserted state
dec dword ptr [ebx].PcPrcbData.PbDpcQueueDepth ; decrement depth
ifndef NT_UP
RELEASE_SPINLOCK esi, NoChecking ; release DPC lock
endif
sti ; enable interrupts
call ecx ; call DPC routine
if DBG
stdCall _KeGetCurrentIrql ; get current IRQL
cmp al, DISPATCH_LEVEL ; check if still at dispatch level
jne rdl55 ; if ne, not at dispatch level
cmp esp, edi ; check if stack pointer is correct
jne rdl60 ; if ne, stack pointer is not correct
mov edi, [esp] ; get starting tick count
add edi, _KiDPCTimeout ; adjust for max dpc time allowed
cmp _KeTickCount, edi ; check if DPC executed too long
jae rdl70 ; if ae, DPC executed too long
rdl30: add esp, 4 * 4 ; remove parameters from stack
pop edi ; restore register
endif
rdl35: cli ; disable interrupts
cmp ebp, [ebp].LsFlink ; check if DPC list is empty
jne rdl10 ; if ne, DPC list not empty
rdl40: mov PCR[PcPrcbData.PbDpcRoutineActive], 0 ; clear DPC routine active
mov [ebx].PcPrcbData.PbDpcInterruptRequested, 0 ; clear DPC requested
;
; Check one last time that the DPC list is empty. This is required to
; close a race condition with the DPC queuing code where it appears that
; a DPC routine is active (and thus an interrupt is not requested), but
; this code has decided the DPC list is empty and is clearing the DPC
; active flag.
;
cmp ebp, [ebp].LsFlink ; check if DPC list is empty
jne rdl5 ; if ne, DPC list not empty
ifndef NT_UP
pop esi ; retore register
endif
ret ; return
;
; Unlock DPC list and clear DPC active.
;
rdl45: ;
ifndef NT_UP
RELEASE_SPINLOCK esi, NoChecking ; release DPC lock
jmp short rdl40 ;
endif
ifndef NT_UP
rdl50: sti ; enable interrupts
SPIN_ON_SPINLOCK esi, <short rdl35> ; spin until lock is freee
endif
if DBG
rdl55: stdCall _KeBugCheckEx, <IRQL_NOT_GREATER_OR_EQUAL, ebx, eax, 0, 0> ;
rdl60: push dword ptr [edi+12] ; push address of DPC function
push offset FLAT:_MsgDpcTrashedEsp ; push message address
call _DbgPrint ; print debug message
add esp, 8 ; remove arguments from stack
int 3 ; break into debugger
mov esp, edi ; reset stack pointer
jmp rdl30 ;
rdl70: mov edx, PCR[PcPrcbData.PbInterruptTime] ; get staring interrupt time
sub edx, [esp+4] ; compute time in DPC routine
jc rdl30 ; if c, interrupt time wrapped
mov ecx, PCR[PcPrcbData.PbInterruptCount] ; get starting interrupt count
sub ecx, [esp+8] ; compute interrupts in while in DPC
mov eax, [esp+12] ; get address of DPC function
push edx ; push interrupt time
push ecx ; push interrupts count
push eax ; push address of DPC function
push offset FLAT:_MsgDpcTimeout ; push message address
call _DbgPrint ; print debug message
add esp, 4 * 4 ; remove arguments from stack
cmp _KdDebuggerEnabled, 0 ; check if debugger enabled
je rdl30 ; if eq, debugger not enabled
call _DbgBreakPoint@0 ; break into debugger
jmp rdl30 ;
endif
KiRetireDpcList endp
_TEXT$00 ends
_TEXT SEGMENT DWORD PUBLIC 'CODE' ; Put IdleLoop in text section
page ,132
subttl "Set up 80387, or allow for emulation"
;++
;
; Routine Description:
;
; This routine is called during kernel initialization once for each
; processor. It sets EM+TS+MP whether we are emulating or not.
;
; If the 387 hardware exists, EM+TS+MP will all be cleared on the
; first trap 07. Thereafter, EM will never be seen for this thread.
; MP+TS will only be set when an error is detected (via IRQ 13), and
; it will be cleared by the trap 07 that will occur on the next FP
; instruction.
;
; If we're emulating, EM+TS+MP are all always set to ensure that all
; FP instructions trap to the emulator (the trap 07 handler is edited
; to point to the emulator, rather than KiTrap07).
;
; Arguments:
;
; None.
;
; Return Value:
;
; None.
;
;--
cPublicProc _KiSetCR0Bits ,0
mov eax, cr0
;
; There are two useful bits in CR0 that we want to turn on if the processor
; is a 486 or above. (They don't exist on the 386)
;
; CR0_AM - Alignment mask (so we can turn on alignment faults)
;
; CR0_WP - Write protect (so we get page faults if we write to a
; write-protected page from kernel mode)
;
cmp byte ptr fs:PcPrcbData.PbCpuType, 3h
jbe @f
;
; The processor is not a 386, (486 or greater) so we assume it is ok to
; turn on these bits.
;
or eax, CR0_WP
@@:
mov cr0, eax
stdRET _KiSetCR0Bits
stdENDP _KiSetCR0Bits
ifdef DBGMP
cPublicProc _KiPollDebugger,0
cPublicFpo 0,3
push eax
push ecx
push edx
POLL_DEBUGGER
pop edx
pop ecx
pop eax
stdRET _KiPollDebugger
stdENDP _KiPollDebugger
endif
_TEXT ends
end