282 lines
8.9 KiB
NASM
282 lines
8.9 KiB
NASM
|
title "System Startup"
|
||
|
;++
|
||
|
;
|
||
|
; Copyright (c) 2000 Microsoft Corporation
|
||
|
;
|
||
|
; Module Name:
|
||
|
;
|
||
|
; start.asm
|
||
|
;
|
||
|
; Abstract:
|
||
|
;
|
||
|
; This module implements the code necessary to initially startup the NT
|
||
|
; system on an AMD64 system.
|
||
|
;
|
||
|
; Author:
|
||
|
;
|
||
|
; David N. Cutler (davec) 22-Sep-2000
|
||
|
;
|
||
|
; Environment:
|
||
|
;
|
||
|
; Kernel mode only.
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
include ksamd64.inc
|
||
|
|
||
|
extern KdInitSystem:proc
|
||
|
extern KeLoaderBlock:qword
|
||
|
extern KiBarrierWait:dword
|
||
|
extern KiIdleLoop:proc
|
||
|
extern KiInitializeBootStructures:proc
|
||
|
extern KiInitializeKernel:proc
|
||
|
extern KiInitialPCR:byte
|
||
|
|
||
|
TotalFrameLength EQU (LEGACY_SAVE_AREA_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 inititialize 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
|
||
|
Fill dq ? ; fill to 8 mode 16
|
||
|
SsFrame ends
|
||
|
|
||
|
NESTED_ENTRY KiSystemStartup, INIT
|
||
|
|
||
|
alloc_stack (sizeof SsFrame) ; allocate stack frame
|
||
|
|
||
|
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
|
||
|
|
||
|
;
|
||
|
; Set connical 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.
|
||
|
;
|
||
|
|
||
|
KiSS10: 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.
|
||
|
;
|
||
|
|
||
|
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 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
|
||
|
|
||
|
;
|
||
|
; Reset stack to include only the space for the legacy NPX state.
|
||
|
;
|
||
|
|
||
|
mov rcx, gs:[PcTss] ; get TSS address
|
||
|
mov rcx, TssRsp0[rcx] ; get idle stack address
|
||
|
lea rsp, (-LEGACY_SAVE_AREA_LENGTH)[rcx] ; deallocate stack space
|
||
|
|
||
|
;
|
||
|
; Enable interrupts, lower IRQL to dispatch level, and set the wait IRQL for
|
||
|
; the idle thread.
|
||
|
;
|
||
|
|
||
|
sti ; enable interrupts
|
||
|
mov ecx, DISPATCH_LEVEL ; set dispatch IRQL
|
||
|
|
||
|
SetIrql ;
|
||
|
|
||
|
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
|
||
|
jnz short KiSS20 ; if nz, barrier set
|
||
|
|
||
|
endif
|
||
|
|
||
|
call KiIdleLoop ; enter idle loop - no return
|
||
|
|
||
|
NESTED_END KisystemStartup, INIT
|
||
|
|
||
|
end
|