Windows2000/private/ntos/ke/i386/newsysbg.asm
2020-09-30 17:12:32 +02:00

1125 lines
33 KiB
NASM

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
include fastsys.inc
FPOFRAME macro a, b
.FPO ( a, b, 0, 0, 0, 0 )
endm
.list
option segment:flat
extrn @ExfInterlockedPopEntrySList@8:DWORD
extrn @ExfInterlockedPushEntrySList@12:DWORD
extrn @ExfInterlockedFlushSList@4:DWORD
extrn @ExInterlockedCompareExchange64@16:DWORD
extrn @ExInterlockedPopEntrySList@8:DWORD
extrn @ExInterlockedPushEntrySList@12:DWORD
extrn @ExInterlockedFlushSList@4: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,7
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,2,IMPORT
EXTRNP HalClearSoftwareInterrupt,1,IMPORT,FASTCALL
if NT_INST
EXTRNP _KiAcquireSpinLock, 1
EXTRNP _KiReleaseSpinLock, 1
endif
EXTRNP KiTryToAcquireQueuedSpinLock,1,,FASTCALL
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
extrn _KeFeatureBits:DWORD
ifndef NT_UP
extrn _KiBarrierWait:DWORD
endif
if DBG
extrn _KdDebuggerEnabled:BYTE
EXTRNP _DbgBreakPoint,0
extrn _DbgPrint:near
extrn _MsgDpcTrashedEsp: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)
; Statically 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)
align 16
public _KiDoubleFaultStack
db DOUBLE_FAULT_STACK_SIZE dup (?)
_KiDoubleFaultStack label byte
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
; 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 usable 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 directory
; LoaderBlock - parameters for this processor
; 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 to 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
mov eax, offset FLAT:_KiDoubleFaultStack
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 to 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 and briefly, it is the DPC stack for the first
; processor.
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,offset FLAT:_KiDoubleFaultStack>
; 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
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, KissLoaderBlock>
mov eax, fs:PcSetMember
or _KeActiveProcessors, eax ; New affinity of active processors
; 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 the 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 instruction 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, @ExInterlockedFlushSList@4 ; get target address
lea ecx, @ExfInterlockedFlushSList@4 ; 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
; Reserve space for idle thread stack NPX_SAVE_AREA and initialization
sub esp, NPX_FRAME_LENGTH+KTRAP_FRAME_LENGTH+KTRAP_FRAME_ALIGN
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 initialization 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 - they 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's 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 processor's call-return stack
; cache.
kid10: lea ecx, [ebx].PcPrcbData.PbPowerState
call dword ptr [ecx].PpIdleFunction ; (ecx) = Arg0
; Give the 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
CheckDpcList0:
YIELD
; 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
; the 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. On MP systems, the dispatcher database is always locked at
; SYNCH level to ensure the lock is held for as short a period as
; possible (reduce contention). On UP systems there really is no
; lock, it is sufficient to be at DISPATCH level (which is the
; current level at this point in the code).
ifndef NT_UP
; see if dispatcher lock is available right now
cmp dword ptr _KiDispatcherLock, 0
jnz short CheckDpcList0
; attempt to acquire the dispatcher database lock
lea ecx, [ebx]+PcPrcbData+PbLockQueue+(8*LockQueueDispatcherLock)
fstCall KiTryToAcquireQueuedSpinLock
jz short CheckDpcList0 ; jif could not acquire lock
; Raise IRQL to synchronization level and enable interrupts.
mov ecx, SYNCH_LEVEL ; raise IRQL to synchronization level
fstCall KfRaiseIrql ;
endif
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 ;
ifndef NT_UP
mov ecx, DISPATCH_LEVEL ; lower IRQL to dispatch level
fstCall KfLowerIrql ;
endif
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
?FpoValue = 0
ifndef NT_UP
?FpoValue = 1
push esi ; save register
lea esi, [ebx].PcPrcbData.PbDpcLock ; get DPC lock address
endif
FPOFRAME ?FpoValue,0
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 of DPC object
mov ecx, [edx].DpDeferredRoutine ; get DPC routine address
if DBG
push edi ; save register
mov edi, esp ; save current stack pointer
endif
FPOFRAME ?FpoValue,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
if DBG
mov PCR[PcPrcbData.PbDebugDpcTime], 0 ; Reset the time in DPC
endif
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
rdl30: pop edi ; restore register
endif
FPOFRAME ?FpoValue,0
rdl35: cli ; disable interrupts
cmp ebp, [ebp].LsFlink ; check if DPC list is empty
jne rdl10 ; if ne, DPC list not empty
; Clear DPC routine active and DPC requested flags.
rdl40: mov [ebx].PcPrcbData.PbDpcRoutineActive, 0
mov [ebx].PcPrcbData.PbDpcInterruptRequested, 0
; 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 ;
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