        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:
;
;   start.asm
;
; Abstract:
;
;   This module implements the code necessary to initially startup the NT
;   system on an AMD64 system.
;
;--

include ksamd64.inc

        extern  KdInitSystem:proc
        extern  KeLoaderBlock:qword
        extern  KiBarrierWait:dword
        extern  KiIdleLoop:proc
        extern  KiInitializeBootStructures:proc
        extern  KiInitializeKernel:proc
        extern  KiInitialPCR:byte
        extern  __security_cookie:qword
        extern  __security_cookie_complement:qword

TotalFrameLength EQU (KERNEL_STACK_CONTROL_LENGTH + KEXCEPTION_FRAME_LENGTH + KSWITCH_FRAME_LENGTH)

        subttl  "System Startup"
;++
;
; Routine Description:
;
;   This routine is called at system startup to perform early initialization
;   and to initialize the kernel debugger. This allows breaking into the
;   kernel debugger very early during system startup. After kernel debugger
;   initialization, kernel initialization is performed. On return from kernel
;   initialization the idle loop is entered. The idle loop begins execution
;   and immediately finds the system startup (phase 1) thread ready to run.
;   Phase 1 initialization is performed and all other processors are started.
;   As each process starts it also passes through the system startup code, but
;   it does not initialization the kernel debugger.
;
; Arguments:
;
;   LoaderBlock (rcx) - Supplies a pointer to the loader block.
;
; Implicit Arguments:
;
;   When the system starts up the loader has done some initialization. In
;   particular all structures have at least been zeroed and the GDT and
;   TSS have been completely initialized.
;
;   The loader block has been reformatted by the loader into a 64-bit loader
;   block and all pertinent fields have been filled in.
;
;   The address of the PRCB is passed in the loader block (only for processors
;   other than zero).
;
;   The address of the idle thread and idle process are passed in the loader
;   block (only for processors other than zero).
;
;   The GDT and IDT address and limits are contained in the gdtr and idtr
;   registers.
;
;   The address of the TSS must be extraced from the appropriate GDT entry
;   and stored in the PCR.
;
;   The stack register (RSP) is loaded with the idle thread stack and the
;   kernel stack field of the loader block contains the address of the DPC
;   stack.
;
; Return Value:
;
;   None - function does not return.
;
;--

SsFrame struct
        P1Home  dq ?                    ;
        P2Home  dq ?                    ;
        P3Home  dq ?                    ;
        P4Home  dq ?                    ;
        P5      dq ?                    ; parameter 5
        P6      dq ?                    ; parameter 6
        SavedR15 dq ?                   ; saved nonvolatile register
SsFrame ends

        NESTED_ENTRY KiSystemStartup, INIT

        alloc_stack (sizeof SsFrame)    ; allocate stack frame
        mov     SsFrame.SavedR15[rsp], r15 ; save nonvolatile register
        set_frame r15, 0                ; set frame register

        END_PROLOGUE

;
; Save the address of the loader block.
;
; N.B. This is the same address for all processors.
;

        mov     KeLoaderBlock, rcx      ; save loader block address

;
; Initialize PCR self address and the current PRCB address.
;

        mov     rdx, LpbPrcb[rcx]       ; get specified PRCB address
        lea     rax, KiInitialPCR + PcPrcb ; get builtin PRCB address
        test    rdx, rdx                ; test if PRCB address specified
        cmovz   rdx, rax                ; if z, set builtin PRCB address
        mov     LpbPrcb[rcx], rdx       ; set loader block PRCB address
        mov     r8, rdx                 ; copy PRCB address
        sub     rdx, PcPrcb             ; compute PCR address
        mov     PcSelf[rdx], rdx        ; set PCR self address
        mov     PcCurrentPrcb[rdx], r8  ; set current PRCB address

;
; Initialize kernel special registers and the address of the GDT, TSS, and
; IDT in the PRCB and PCR.
;
; N.B. The debug registers are zeroed in the PRCB.
;

        mov     r8, cr0                 ; save CR0
        mov     PcCr0[rdx], r8          ;
        mov     r8, cr2                 ; save CR2
        mov     PcCr2[rdx], r8          ;
        mov     r8, cr3                 ; save CR3
        mov     PcCr3[rdx], r8          ;
        mov     r8, cr4                 ; save CR4
        mov     PcCr4[rdx], r8          ;

        sgdt    PcGdtrLimit[rdx]        ; save GDT limit and base
        mov     r8, PcGdtrBase[rdx]     ; set GDT base address
        mov     PcGdt[rdx], r8          ;
        sidt    PcIdtrLimit[rdx]        ; save IDT limit and base
        mov     r9, PcIdtrBase[rdx]     ; set IDT base address
        mov     PcIdt[rdx], r9          ;

        str     word ptr PcTr[rdx]      ; save TR selector
        sldt    word ptr PcLdtr[rdx]    ; save LDT selector

        mov     dword ptr PcMxCsr[rdx], INITIAL_MXCSR ; set initial MXCSR
        ldmxcsr PcMxCsr[rdx]            ;

;
; Set canonical selector values (note CS, GS, and SS are already set).
;

        mov     ax, KGDT64_R3_DATA or RPL_MASK ;
        mov     ds, ax                  ;
        mov     es, ax                  ;
        mov     ax, KGDT64_R3_CMTEB or RPL_MASK ;
        mov     fs, ax                  ;

;
; Load a NULL selector into the LDT.
;

        xor     eax, eax                ; set NULL selector for LDT
        lldt    ax                      ; 

;
; Extract TSS address from GDT entry and store in PCR.
;

        mov     ax, KGDT64_SYS_TSS + KgdtBaseLow[r8] ; set low 16-bits
        mov     PcTss[rdx], ax          ;
        mov     al, KGDT64_SYS_TSS + KgdtBaseMiddle[r8] ; set middle 8-bits
        mov     PcTss + 2[rdx], al      ;
        mov     al, KGDT64_SYS_TSS + KgdtBaseHigh[r8] ; set high 8-bits
        mov     PcTss + 3[rdx], al      ;
        mov     eax, KGDT64_SYS_TSS +KgdtBaseUpper[r8] ; set upper 32-bits
        mov     PcTss + 4[rdx], eax     ;

;
; Initialize the GS base and swap addresses.
;

        mov     eax, edx                ; set low 32-bits of address
        shr     rdx, 32                 ; set high 32-bits of address
        mov     ecx, MSR_GS_BASE        ; get GS base address MSR number
        wrmsr                           ; write GS base address
        mov     ecx, MSR_GS_SWAP        ; get GS swap base MSR number
        wrmsr                           ; write GS swap base address

;
; Initialize boot structures.
;

        mov     rcx, KeLoaderBlock      ; set loader block address
        call    KiInitializeBootStructures ; initialize boot structures

;
; Initialize the kernel debugger if this is processor zero.
;

        xor     ecx, ecx                ; set phase to 0
        mov     rdx, KeLoaderBlock      ; set loader block address
        call    KdInitSystem            ; initialize debugger

;
; Raise IRQL to high level and initialize the kernel.
;

        mov     ecx, HIGH_LEVEL         ; set high IRQL

        SetIrql                         ;

;
; Reserve space for idle thread stack initialization.
;
; N.B. This reservation ensures that the initialization of the thread stack
;      does not overwrite any information on the current stack which is the
;      same stack.
;

        sub     rsp, TotalFrameLength   ; allocate stack

;
; Initialize kernel.
;
; N.B. Kernel initialization is called with interupts disabled at IRQL
;      HIGH_LEVEL and returns with interrupt enabled at IRQL DISPATCH_LEVEL.
;

        mov     rax, KeLoaderBlock      ; set loader block address
        mov     rcx, LpbProcess[rax]    ; set idle process address
        mov     rdx, LpbThread[rax]     ; set idle thread address
        mov     r8, gs:[PcTss]          ; set idle stack address
        mov     r8, TssRsp0[r8]         ;
        mov     gs:[PcRspBase], r8      ; set initial stack address in PRCB
        mov     r9, LpbPrcb[rax]        ; set PRCB address
        mov     r10b, PbNumber[r9]      ; set processor number
        mov     SsFrame.P5[rsp], r10    ;
        mov     SsFrame.P6[rsp], rax    ; set loader block address
        call    KiInitializeKernel      ; Initialize kernel

;
; If processor zero is being initialized, then save the GS cookie value.
;

        cmp     byte ptr gs:[PcNumber], 0 ; check for processor zero
        jne     short @f                ; if ne, not processor zero
        rdtsc                           ; read time stamp counter
        shl     rdx, 32                 ; merge low and high part
        or      rax, rdx                ;
        mov     rdx, rax                ; copy result and rotate
        ror     rdx, 49                 ;
        xor     rax, rdx                ; randomize upper bits
        rol     rax, 16                 ; clear high work of result
        mov     ax, 0                   ;
        ror     rax, 16                 ;
        mov     __security_cookie, rax  ; save GS cookie value
        not     rax                     ; complement cookie value
        mov     __security_cookie_complement, rax ; save GS complement value

;
; Reset stack to include only the space for the legacy NPX state.
;

@@:     mov     rcx, gs:[PcRspBase]     ; get idle stack address
        lea     rsp, (-KERNEL_STACK_CONTROL_LENGTH)[rcx] ; deallocate stack space

;
; Set the wait IRQL for the idle thread.
;

        mov     rcx, gs:[PcCurrentThread] ; get current thread address
        mov     byte ptr ThWaitIrql[rcx], DISPATCH_LEVEL ; set wait IRQL

;
; In a multiprocessor system the boot processor proceeds directly into the
; idle loop. As other processors start executing, however, they do not enter
; the idle loop directly - they spin until all processors have been started
; and the boot master allows them to proceed.
;

ifndef NT_UP

KiSS20: cmp     KiBarrierWait, 0        ; check if barrier set

        Yield                           ; yield processor execution

        jnz     short KiSS20            ; if nz, barrier set

endif

        call    KiIdleLoop              ; enter idle loop - no return

        NESTED_END KisystemStartup, INIT

        end

