        title  "System Startup"
;++
;
; Copyright (c) Microsoft Corporation. All rights reserved. 
;
; You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
; If you do not agree to the terms, do not use the code.
;
;
; Module Name:
;
;    systembg.asm
;
; Abstract:
;
;    This module implements the code necessary to initially startup the
;    NT system.
;
;--
.386p
        .xlist
include ks386.inc
include i386\kimacro.inc
include mac386.inc
include callconv.inc
include irqli386.inc
        .list

        option  segment:flat

        extrn   _KiBootFeatureBits:DWORD
        EXTRNP  _KdInitSystem,2
        EXTRNP  _KdPollBreakIn,0
        EXTRNP  _KiInitializeKernel,6
        EXTRNP  GetMachineBootPointers
        EXTRNP  KiIdleLoop,0,,FASTCALL
        EXTRNP  _KiInitializePcr,7
        EXTRNP  _KiSwapIDT
        EXTRNP  _KiInitializeTSS,1
        EXTRNP  _KiInitializeTSS2,2
        extrn   _KiTrap08:PROC
        extrn   _KiTrap02:PROC
        EXTRNP  _KiInitializeAbios,1
        EXTRNP  _KiInitializeMachineType
        EXTRNP  _HalInitializeProcessor,2,IMPORT
        extrn   _KiFreezeExecutionLock:DWORD
        extrn   _IDT:BYTE
        extrn   _IDTLEN:BYTE            ; NOTE - really an ABS, linker problems
        extrn   _KeNumberProcessors:BYTE
        extrn   _KeActiveProcessors:DWORD
        extrn   _KeLoaderBlock:DWORD
        extrn   _KiInitialProcess:BYTE
        extrn   _KiInitialThread:BYTE

ifndef NT_UP
        extrn   _KiBarrierWait:DWORD
        EXTRNP  _KiProcessorStart
endif


;
; Constants for various variables
;

_DATA   SEGMENT PARA PUBLIC 'DATA'

;
; Statically allocated structures for Bootstrap processor
; double fault stack for P0
; idle thread stack for P0
;

        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 _KiInitialThread
        mov     dword ptr [ebx].LpbKernelStack, offset P0BootStack

        push    KGDT_R0_PCR                 ; P0 needs FS set
        pop     fs

        ; Save processornumber in Prcb
        mov     byte ptr PCR[PcPrcbData+PbNumber], cl
@@:
        mov     eax, dword ptr [ebx].LpbThread
        mov     dword ptr KissIdleThread, eax

        lea     ecx, [eax].ThApcState.AsApcListHead ; initialize kernel APC list head
        mov     [eax].ThApcState.AsApcListHead, ecx ;
        mov     [eax].ThApcState.AsApcListHead+4, ecx ;

        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
;       );
        push      edx
        stdCall   _KiInitializeTSS, <edx>
        pop       edx

        mov     eax,cr3
        mov     [edx+TssCr3],eax

        mov     eax, offset FLAT:_KiDoubleFaultStack
        mov     dword ptr [edx+TssEsp],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
                                        ;       );
        pop     edx

;
; 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:_KiInitialProcess ; (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 PCR[PcTeb], 0   ; PCR->Teb = 0

;
; Initialize KernelDr7 and KernelDr6 to 0.  This must be done before
; the debugger is called.
;

        mov     dword ptr PCR[PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr6],0
        mov     dword ptr PCR[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]

ifdef QLOCK_STAT_GATHER

        EXTRNP  KiCaptureQueuedSpinlockRoutines,0,,FASTCALL

        fstCall KiCaptureQueuedSpinlockRoutines

endif

kiss_notp0:

ifndef NT_UP

;
; Let the boot processor know this processor is starting.
;

        stdCall _KiProcessorStart

endif

;
; 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
;

@@:     test    _KiFreezeExecutionLock, 1
        jnz     short @b

        lock bts _KiFreezeExecutionLock, 0
        jc      short @b

;
; Add processor to active summary, and update BroadcastMasks
;
        mov     ecx, dword ptr KissPbNumber ; mark this processor as active
        mov     byte ptr PCR[PcNumber], cl
        mov     eax, 1
        shl     eax, cl                     ; our affinity bit
        mov     PCR[PcSetMember], eax
        mov     PCR[PcSetMemberCopy], eax
        mov     PCR[PcPrcbData.PbSetMember], eax

;
; Initialize the interprocessor interrupt vector and increment ready
; processor count to enable kernel debugger.
;
        stdCall   _HalInitializeProcessor, <dword ptr KissPbNumber, KissLoaderBlock>

ifdef _APIC_TPR_

;
; Record the IRQL tables passed to us by the hal
;

        mov     eax, KissLoaderBlock
        mov     eax, [eax]+LpbExtension
        mov     ecx, [eax]+LpeHalpIRQLToTPR
        mov     _HalpIRQLToTPR, ecx
        mov     ecx, [eax]+LpeHalpVectorToIRQL
        mov     _HalpVectorToIRQL, ecx

endif


        mov     eax, PCR[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

        xor     eax, eax                    ; release the executionlock
        mov     _KiFreezeExecutionLock, eax

        cmp     byte ptr KissPbNumber, 0
        jnz     @f

; don't stop in debugger
        stdCall   _KdInitSystem, <0,_KeLoaderBlock>

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
;

        RaiseIrql HIGH_LEVEL
        mov     KissIrql, al
        or      _KiBootFeatureBits, KF_CMPXCHG8B ; We're committed to using

;
; 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 _KiInitialProcess,ebx,edx,dword ptr PCR[PcPrcb],eax,_KeLoaderBlock>

;
; Set idle thread priority.
;

        mov     ebx,PCR[PcPrcbData+PbCurrentThread] ; get idle thread address
        mov     byte ptr [ebx]+ThPriority, 0 ; set idle thread priority

;
; 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
        LowerIrql DISPATCH_LEVEL
        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
        YIELD
        jnz     short @b                ; if nz, barrier set

endif

        push    0                       ; terminate KD traceback 0 RA.
        jmp     @KiIdleLoop@0           ; enter idle loop

stdENDP _KiSystemStartup

        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 PCR[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

INIT    ends

        end

