天天看點

Windows NT引導過程源代碼分析(一)

轉載自:https://blog.csdn.net/cosmoslife/article/details/7855425

Windows 引導過程

Windows 核心中的各個元件和各種機制在起作用以前,必須首先被初始化。此初始化工作是在系統引導時完成的。當使用者打開計算機的電源開關時,計算機便開始運作,但作業系統并不立即獲得控制權,而是BIOS 代碼首先獲得控制,它執行必要的硬體檢測工作,并允許使用者通過一些功能鍵來配置目前系統中的硬體設定,甚至診斷硬體問題,然後才将控制權交給作業系統。

1.1 核心加載

在Intel x86 系統上,Windows 作業系統獲得控制首先從硬碟的主引導記錄(MBR,Master Boot Record)開始,Windows Setup 程式在安裝Windows 時填充MBR(其他的磁盤管理程式也可能填充MBR)。MBR 包含代碼和資料,其代碼稱為引導代碼,在系統引導時首先獲得控制;MBR 中的資料是一張分區表,指定了每個分區在磁盤上的位置和大小,以及分區的類型。當MBR 中的引導代碼被執行時,它檢查分區表中的每一個分區,若找到一個已被标記為可引導的分區(稱為引導分區),則将該分區的第一個扇區(稱為引導扇區)讀到記憶體中。由于分區表包含了每一個分區的磁盤位置,是以,引導扇區的位置很容易被确定。然後MBR 的代碼将控制權交給引導扇區中的代碼。

;此處代碼摘自NT4代碼的\private\ntos\boot\bootcode\x86mboot.asm

relocated_org   equ 0600h

buildtime_org   equ 0100h

org_delta       equ (relocated_org - buildtime_org)

_data   segment public

        assume  cs:_data,ds:_data

        org buildtime_org

; 這段代碼讀出位于主引導記錄末尾的分區表,找到标志為可引導的分區,把它的引導扇區拷貝到記憶體中并執行

start:

        cli             ;開始的時候并沒有中斷

        xor ax,ax

        mov ss,ax

        mov sp,7c00h    ;位于位址0:7c00處的新堆棧

        mov si,sp        ; 0:7c00為标準引導位址  

        push ax

        pop es          

        push ax        

        sti             ;允許中斷

        cld

        mov di,relocated_org         mov cx,100h

        rep movsw       

 ;重定位到位址 0000:0600,跳到這裡從分區表中讀取可引導分區的入口,把引導分區拷貝到記憶體的标準引導位址(0000:7C00)

;       jmp entry2 + org_delta

        db   0eah

        dw   $+4+org_delta,0

entry2:

        mov si,(offset tab) + org_delta  ;表示分區表

        mov bl,4        ;分區表項的個數

next:

        cmp byte ptr[si],80h  ;判斷是否是可以引導的入口

        je boot         ;yes

        cmp byte ptr[si],0    ;再次判斷是否為0

        jne bad         ;不是,隻有 x"00" 或者x"80" 是有效的

        add si,16       ;執行到下一個入口點

        dec bl

        jnz next

        int 18h         ;未檢測到可引導的入口,傳回

boot:

        mov dx,[si]     ;引導開始處

        mov cx,[si+2]  

        mov bp,si       ;儲存表入口位址并傳給分區引導記錄

next1:

        add si,16       ;下一個表項

        dec bl          ;表項數目遞減

        jz tabok        

        cmp byte ptr[si],0    ;所有剩餘的表入口都要從0開始

        je next1        ;滿足上述判斷條件

bad:

        mov si,(offset m1) + org_delta ;無法找到一個從0開始的表項入口,該表為壞表

msg:

        lodsb           ;擷取顯示資訊的字元

        cmp al,0

        je  hold

        push si

        mov bx,7

        mov ah,14

        int 10h          ;顯示資訊

        pop si

        jmp msg         ;循環列印完整資訊

hold:   jmp hold        ;此處自旋,不做任何事

tabok:

        mov di,5        ;計數值

rdboot:

        mov bx,7c00h    ;讀取系統引導記錄的位置

        mov ax,0201h    ;讀取一個扇區

        push di

        int 13h          ; 擷取引導記錄

        pop di

        jnc goboot      ;成功得到引導記錄,交與控制權

        xor ax,ax        ;出現錯誤

        int 13h          ;重新校準

        dec di           ;遞減計數值

        jnz rdboot       ;隻要計數值仍大于0,就繼續嘗試

        mov si,(offset m2) + org_delta ;所有的入口都已檢測完畢,錯誤無法避免

        jmp msg          ;跳轉到顯示錯誤資訊

goboot:

        mov si,(offset m3) + org_delta 

        mov di,07dfeh

        cmp word ptr [di],0aa55h ;判斷引導記錄是否有效

        jne msg         ;無效,則顯示無效的系統引導記錄資訊

        mov si,bp       ;有效,則将分區表入口位址傳給它

        db 0eah

        dw 7c00h,0

include x86mboot.msg 

        org 2beh     ;此處顯示了主引導記錄的結構

tab:                     ;partition table

        dw 0,0          ;partition 1 begin

        dw 0,0          ;partition 1 end

        dw 0,0          ;partition 1 relative sector (low, high parts)

        dw 0,0          ;partition 1 # of sectors (low, high parts)

        dw 0,0          ;partition 2 begin

        dw 0,0          ;partition 2 end

        dw 0,0          ;partition 2 relative sector

        dw 0,0          ;partition 2 # of sectors

        dw 0,0          ;partition 3 begin

        dw 0,0          ;partition 3 end

        dw 0,0          ;partition 3 relative sector

        dw 0,0          ;partition 3 # of sectors

        dw 0,0          ;partition 4 begin

        dw 0,0          ;partition 4 end

        dw 0,0          ;partition 4 relative sector

        dw 0,0          ;partition 4 # of sectors

signa   db 55h,0aah     ;引導區有效簽名值

_data   ends

        end  start

Windows Setup 程式在确定了要将Windows 系統安裝到哪個分區中以後,除了可能會寫入MBR 以外,還會寫入該分區的引導扇區。是以,嚴格意義上講,Windows 作業系統的真正入口點應該是引導扇區中的代碼。引導分區必須被格式化成Windows 所支援的檔案系統,典型的檔案系統格式是NTFS 和FAT,其中NTFS 是Windows NT 的原生檔案系統,而FAT 則是從MS-DOS 時代繼承和發展過來的。

引導扇區中的代碼随硬碟檔案系統格式的不同而有所不同,其職責是,給Windows提供有關該硬碟上卷的結構和格式方面的資訊,并且從該卷的根目錄中讀入Windows 的加載程式,即ntldr 檔案;然後将控制權交給ntldr 的入口函數。為了能夠從根目錄中讀入加載程式,引導扇區包含了能了解檔案系統結構和讀取檔案的代碼,這通常隻是檔案系統極其簡單的一部分功能,而并非完整的實作。盡管引導扇區的職責相對簡單,但是單個扇區(512 B)的代碼和資料往往不足以完成其功能,為此,Windows 的做法是,讓引導扇區中的代碼讀入其他扇區的資料,然後跳轉到下一個扇區的代碼區。這樣就可以不受單個引導扇區長度的限制,這種做法相當于将第一個引導扇區當做一個加載器(loader),而真正完成引導扇區功能的扇區随後被加載進來并執行。這一過程對于MBR 是透明的,進而保持良好的相容性。

; 此處代碼摘自NT4代碼的\private\ntos\boot\bootcode\ntfs\i386\ntfsboot.asm

MASM    equ     1

        .xlist

        .286

A_DEFINED EQU 1

    include ntfs.inc

DoubleWord      struc

lsw     dw      ?

msw     dw      ?

DoubleWord      ends

; 下面的代碼顯示了幾個引導加載器使用的不同區段,最開始的兩個分别是引導扇區最先加載的位置以及之後重定位的位置

; 第三個則是NTLDR加載的靜态位址

BootSeg segment at 07c0h          ; ROM 起先加載的位置.

BootSeg ends

NewSeg  segment at 0d00h        ; 重定位的位置.

NewSeg  ends                     

LdrSeg segment at 2000h           ; 将要在位址 2000:0000處加載加載器

LdrSeg ends

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

;

BootCode segment        

        assume  cs:BootCode,ds:nothing,es:nothing,ss:nothing

        org     0               

        public  _ntfsboot

_ntfsboot proc   far

        jmp     start

    .errnz  ($-_ntfsboot) GT (3),<FATAL PROBLEM: JMP is more than three bytes>

    org 3

;  這是一個參數塊的模版 – 任何調用者将引導代碼寫入磁盤以後都應該儲存一個存在的參數塊以及NTFS資訊或者建立一個新的

Version                 db      "NTFS    "      ; Must be 8 characters

BPB                     label   byte

BytesPerSector          dw      0               ; Size of a physical sector

SectorsPerCluster       db      0               ; Sectors per allocation unit

ReservedSectors         dw      0               ; Number of reserved sectors

Fats                    db      0               ; Number of fats

DirectoryEntries        dw      0               ; Number of directory entries

Sectors                 dw      0               ; No. of sectors - no. of hidden sectors

Media                   db      0               ; Media byte

FatSectors              dw      0               ; Number of fat sectors

SectorsPerTrack         dw      0               ; Sectors per track

Heads                   dw      0               ; Number of surfaces

HiddenSectors           dd      0               ; Number of hidden sectors

SectorsLong             dd      0               ; Number of sectors iff Sectors = 0

; The following is the rest of the NTFS Sector Zero information.

; The position and order of DriveNumber and CurrentHead are especially important

; since those two variables are loaded into a single 16-bit register for the BIOS with one instruction.

DriveNumber         db      80h             ; Physical drive number (0 or 80h)

CurrentHead         db      ?               ; Variable to store current head no.

SectorZeroPad1      dw      0

SectorsOnVolume     db (size LARGE_INTEGER) dup (0)

MftStartLcn         db (size LARGE_INTEGER) dup (0)

Mft2StartLcn        db (size LARGE_INTEGER) dup (0)

ClustersPerFrs      dd      0

DefClustersPerBuf   dd      0

SerialNumber        db (size LARGE_INTEGER) dup (0)

CheckSum            dd      0

; The following variables are not part of the Extended BPB;  they're just scratch variables for the boot code.

SectorBase      dd      ?               ; next sector to read

CurrentTrack    dw      ?               ; current track

CurrentSector   db      ?               ; current sector

SectorCount     dw      ?               ; number of sectors to read

start:

; 首先設定需要用到的區段(堆棧和資料).

        cli

        xor     ax, ax                    ; 設定堆棧起點為該代碼的前一句,在重定位之後将會被轉移

        mov     ss, ax                           

mov     sp, 7c00h               . 

        Sti

; BIOS把可引導磁盤(磁道0,磁頭0,扇區1)的第一個實體扇區映射到記憶體中實體位址為7C00處

        mov     ax, Bootseg             

        mov     ds, ax

        assume  ds:BootCode

;  開始将引導塊内容讀入記憶體,然後跳轉至新版本的引導塊,位于第二扇區開始處

        mov     SectorBase.lsw, 0       ; 讀取扇區0.

        mov     SectorBase.msw, 0

        mov     word ptr [SectorCount], 16 ; 讀取引導區域代碼

        mov     ax, NewSeg               ; 在NewSeg處讀取.

        mov     es, ax

        sub     bx, bx                    ; 定位NewSeg:0000.

        call    DoReadLL                 ; 調用底層的DoRead例程,該部分讀取扇區的代碼從略

        push    NewSeg                 ; 調整到 NewSeg:0200h.

        push    offset mainboot          ; 壓入第二個扇區的位址

        ret                               ; 傳回到第二個扇區

_ntfsboot endp

Intel x86 處理器支援實模式和保護模式,在實模式下,處理器的寄存器都是16 位的,而且不支援虛拟位址轉譯,隻能通路實體記憶體空間中最低的1 MB 記憶體。計算機系統的BIOS 工作在實模式下,并且,當ntldr 獲得控制權時,處理器仍然在實模式下運作。Ntldr檔案實際上是由兩部分組成的:第一部分是實模式代碼,即首先獲得控制的代碼區;第二部分是一個标準的Windows 可執行二進制檔案,在ntldr 中這部分被稱為os loader。

; 此處代碼摘自NT4代碼的\private\\ntos\boot\startup\i386\su.asm

; _EnableProtectPaging

; 加載386保護模式寄存器

; 啟用386保護模式

; 加載分頁寄存器

; 啟用386分頁機制

public _EnableProtectPaging

_EnableProtectPaging  proc near

        push     dword ptr 0

        popfd

        mov      bx,sp

        mov      dx,[bx+2]  ; 檢測是否是第一次開啟保護模式以及分頁機制

        xor      ax,ax

        mov      gs,ax

        mov      es,ax

; 當調用核心的時候FS必須包含PCR的選擇字

        push    PCR_Selector

        pop     fs

;加載gdtr和idtr

;在這裡禁用中斷,因為無法在轉換到保護模式之前位于實模式且idt已被載入的情況下進行中斷

        cli

        lgdt     fword ptr [_GDTregister]

        lidt     fword ptr [_IDTregister]

; We have to stamp the segment portion of any real-mode far pointer with the corresponding selector values before we go protected.

        mov      si,offset _ScreenStart

        mov      word ptr [si+2],VideoSelector

        mov      si,offset _vp

        mov      word ptr [si+2],VideoSelector

; 開啟保護模式和分頁機制

        mov      eax,cr0

; 如果是第一次開啟保護模式,那麼無需開啟分頁機制,因為osloader已經做好一切

; 如果代碼是傳回保護模式,分頁表已經設定完畢,同樣無需開啟

        or      dx,dx

        jz      only_prot

        or      eax,PROT_MODE + ENABLE_PAGING

        mov     cr0,eax

; 接下來代碼中的JMP必須是雙字對齊,為了避免觸發一個i386的硬體bug

; 否則有可能使得預取隊列混亂

ALIGN 4

        jmp     flush

only_prot:

        or       eax,PROT_MODE

        mov      cr0,eax

; 重新整理預取隊列

ALIGN 4

        jmp     flush

flush:

; 将寄存器CS作為SU子產品的代碼選擇子

        push    SuCodeSelector

        push    offset cs:restart

        retf

; 将寄存器DS和SS作為SU子產品的保護模式資料選擇子.

restart:

        mov      ax,SuDataSelector

        mov      ds,ax

        mov      ss,ax

; 加載LDT為0,因為從未被使用.

        xor      bx,bx

        lldt     bx

; 加載任務寄存器

        or       dx,dx

        jnz      epp10

        mov      bx,TSS_Selector

        ltr      bx

epp10:

        ret    ;傳回之後介紹的su.asm中的子產品

_EnableProtectPaging endp

public _RealMode

_RealMode proc near

; 轉換到實模式

        sgdt     fword ptr [_GDTregister]

        sidt     fword ptr [_IDTregister]

        push     [saveDS]          ; 将saveDS入棧,友善之後的跳轉

        mov      ax,SuDataSelector

        mov      es,ax

        mov      fs,ax

        mov      gs,ax

        mov      eax,cr0

        and      eax, not (ENABLE_PAGING + PROT_MODE)

        mov      cr0,eax

; 重新整理流水線

        jmp     far ptr here

here:

; Flush TLB

; We don't know where the page directory is, since it was allocated in the osloader.  

; So we don't want to clear out cr3, but we DO want to flush the TLB....

        mov     eax,cr3

        nop                             ; Fill - Ensure 13 non-page split

        nop                             ; accesses before CR3 load

        nop                             

        nop

        mov     cr3,eax

; 轉換為實模式位址

; 此處需要一個遠跳轉而不是指令retf,因為retf不能正确地重置通路權限為CS

        db      0EAh                    ; JMP FAR PTR

        dw      offset _TEXT:rmode      ; 2000:rmode

        dw      02000h

rmode:

        pop      ax

        mov      ds,ax

        mov      ss,ax

; Stamp video pointers for real-mode use

        mov     si,offset _ScreenStart

        mov     word ptr [si+2],0b800h

        mov     si,offset _vp

        mov     word ptr [si+2],0b800h

        lidt    fword ptr [_IDTregisterZero]

        sti

        ret

_RealMode endp

Ntldr 的實模式代碼首先獲得控制,它的任務是,完成需在16 位模式下執行的初始化工作,例如清除鍵盤緩沖區,然後為切換到保護模式做好基本的環境準備,之後将處理器切換到保護模式(32 位模式)下,這樣它就可以通路整個32 位位址空間了。最後它将控制權交給os loader。

; _TransferToLoader     ;該子程式将控制權交給osloader

public _TransferToLoader

_TransferToLoader proc near

        mov      ebx,dword ptr [esp+2]      ; 擷取入口點參數

        xor      eax,eax

        mov      ax,[saveDS]

; 設定osloader的堆棧

        mov      cx,KeDataSelector

        mov      ss,cx

        mov      esp,LOADER_STACK  

; 加載ds和es作為核心資料選擇子

        mov      ds,cx

        mov      es,cx

; 設定指向檔案系統和引導上下文記錄的指針

        shl      eax,4

        xor      ecx,ecx

        mov      cx,offset _BootRecord

        add      eax,ecx

        push     eax

        push     1010h       ; 壓入假的傳回位址

; 将一個48位的位址傳給loader的入口點

        db OVERRIDE

        push    KeCodeSelector

        push    ebx

; 将控制權交還OS loader

        db OVERRIDE

        retf

_TransferToLoader endp

Os loader 剛接獲控制時,處理器雖然已經工作在保護模式下,但是它的虛拟位址轉譯機制尚未開啟,是以,處理器仍然直接使用實體位址。Os loader 首先做的工作是把實體記憶體管起來,用一個記憶體描述符(memory descriptor)數組把每一段記憶體的大小和用途記錄下來,然後構造頁目錄和頁表,使得16 MB 以下的記憶體能夠通過頁面映射(paging)機制進行通路,再設定好頁目錄寄存器,并打開頁面映射機制。之後,os loader 繼續執行其他的初始化工作,包括I/O 裝置的初始化等。如果它還需要調用BIOS 中的服務(比如中斷13h、中斷15h 等),則必須保護好保護模式下的設定,并暫時切換回到實模式,待服務完成以後再切換到保護模式,并恢複設定。

Windows 的引導選項可以用來訓示目前這次引導的各種參數,包括核心子產品的檔案名稱、HAL 的檔案名稱、CPU 參數、各種記憶體參數、調試參數,等等。關于這些引導選項的全面清單和介紹,可參考[MSDN-BOOT]。接下來os loader 加載并執行NTDETECT.COM 程式,這是一個16 位實模式程式,它利用系統的BIOS 來查詢系統的基本裝置和配置資訊,包括系統的日期和時間、總線的類型、磁盤的資訊、輸入/輸出的接口資訊等。這些資訊被收集起來,在引導過程的後期被存放到系統資料庫HKLM\HARDWARE\DESCRIPTION 鍵的下面。

代碼摘自\ntos\boot\startup\i386\main.c

VOID

SuMain(

    IN FPVOID BtRootDir,

    IN FPDISKBPB BtBiosBlock,

    IN SHORT BtBootDrive

)

{

    ULONG LoaderEntryPoint;

    ULONG EisaNumPages;

    USHORT IsaNumPages;

    MEMORY_LIST_ENTRY _far *CurrentEntry;

    PIMAGE_OPTIONAL_HEADER OptionalHeader;

    ULONG BlockEnd;

    ULONG ImageSize;

    ULONG ImageBase;

    // 儲存檔案系統上下文資訊

    FsContext.BootDrive = (ULONG)BtBootDrive;

    FsContext.PointerToBPB = MAKE_FLAT_ADDRESS(BtBiosBlock);

// 初始化視訊子系統以使得錯誤和異常資訊可以被顯示

    InitializeVideoSubSystem();

// 如果系統由軟碟引導,那麼關掉軟碟驅動器

    TurnMotorOff();

    PatchDiskBaseTable();

    // 基于總線類型設定機器類型.

    if (BtIsEisaSystem()) {

        MachineType = MACHINE_TYPE_EISA;

    } else {

        if (BtIsMcaSystem()) {

            MachineType = MACHINE_TYPE_MCA;

        } else {

            MachineType = MACHINE_TYPE_ISA;

        }

}

    if (!ConstructMemoryDescriptors()) {

        if (MachineType == MACHINE_TYPE_EISA) {

            IsaNumPages = IsaConstructMemoryDescriptors();

            EisaNumPages = EisaConstructMemoryDescriptors();

            if (EisaNumPages + 0x80 < IsaNumPages) {

                IsaConstructMemoryDescriptors();

            }

        } else {

            if (MachineType == MACHINE_TYPE_MCA) {

                McaConstructMemoryDescriptors();

            } else {

                IsaConstructMemoryDescriptors();

            }

        }

}

     // 搜尋記憶體描述符來表示低記憶體

    CurrentEntry = MemoryDescriptorList;

    while ((CurrentEntry->BlockBase != 0) &&

           (CurrentEntry->BlockSize != 0)) {

        CurrentEntry++;

    }

    if ((CurrentEntry->BlockBase == 0) &&

        (CurrentEntry->BlockSize < (ULONG)512 * (ULONG)1024)) {

        BlPrint(SU_NO_LOW_MEMORY,CurrentEntry->BlockSize/1024);

        while (1) {

        }

}

// 確定os loader映像檔案包含一個記憶體描述符

    OptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)&edata + sizeof(IMAGE_FILE_HEADER));

    ImageBase = OptionalHeader->ImageBase;

    ImageSize = OptionalHeader->SizeOfImage;

    OsLoaderBase = ImageBase;

    OsLoaderExports = ImageBase + OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    CurrentEntry = MemoryDescriptorList;

    while (ImageSize > 0) {

        while (CurrentEntry->BlockSize != 0) {

            BlockEnd = CurrentEntry->BlockBase + CurrentEntry->BlockSize;

            if ((CurrentEntry->BlockBase <= ImageBase) &&

                (BlockEnd > ImageBase)) {

                // 該描述符至少得包含osloader的一部分代碼

                if (BlockEnd-ImageBase > ImageSize) {

                    ImageSize = 0;

                } else {

                    ImageSize -= (BlockEnd-ImageBase);

                    ImageBase = BlockEnd;

                }

                // 尋找剩餘一部分的代碼

                CurrentEntry = MemoryDescriptorList;

                break;

            }

            CurrentEntry++;

        }

        if (CurrentEntry->BlockSize == 0) {

            break;

        }

    }

if (ImageSize > 0) {

        // 不能将osloader重定位到高記憶體位置,否則輸出錯誤資訊

        BlPrint(SU_NO_EXTENDED_MEMORY);

        CurrentEntry = MemoryDescriptorList;

        while (CurrentEntry->BlockSize != 0) {

            BlPrint("    %lx - %lx\n",

                    CurrentEntry->BlockBase,

                    CurrentEntry->BlockBase + CurrentEntry->BlockSize);

            CurrentEntry++;

        }

        while (1) {

        }

    }

// 啟用A20線

    EnableA20();

// 重定位保護模式中使用的IDT和GDT結構

    Relocatex86Structures();

// 首次開啟保護模式和分頁模式

    EnableProtectPaging(ENABLING);

    // 重定位代碼段并建立頁面表項

    LoaderEntryPoint = RelocateLoaderSections(&OsLoaderStart, &OsLoaderEnd);

// 将控制權交給OS loader

    TransferToLoader(LoaderEntryPoint);

}

VOID

NtProcessStartup(

    IN PBOOT_CONTEXT BootContextRecord

    )

{

    ARC_STATUS Status;

    // 初始化引導加載器的顯示功能

    DoGlobalInitialization(BootContextRecord);

    BlFillInSystemParameters(BootContextRecord);

    if (BootContextRecord->FSContextPointer->BootDrive == 0) {

        // 從磁盤A:開始嘗試引導

        strcpy(BootPartitionName,"multi(0)disk(0)fdisk(0)");

        GET_SECTOR(0,0,0,0,0,0,NULL);

#if defined(ELTORITO)

    } else if (BlIsElToritoCDBoot(BootContextRecord->FSContextPointer->BootDrive)) {

        // 從CD開始嘗試引導

        sprintf(BootPartitionName, "multi(0)disk(0)cdrom(%u)", BootContextRecord->FSContextPointer->BootDrive);

        ElToritoCDBoot = TRUE;

#endif

    } else {

        //檢查引導成功的分區是哪一個 

        BlGetActivePartition(BootPartitionName);

}

// 初始化記憶體描述符清單,OS loader的堆和參數塊

    Status = BlMemoryInitialize();

    if (Status != ESUCCESS) {

        BlPrint("Couldn't initialize memory\n");

        while (1) {

        }

    }

// 初始化OS loader和I/O系統

    Status = BlIoInitialize();

    if (Status != ESUCCESS) {

        BlPrint("Couldn't initialize I/O\n");

    }

    BlStartup(BootPartitionName);

    // 永遠不應該運作到這裡!

    do {

        GET_KEY();

    } while ( 1 );

}

BOOLEAN

BlDetectHardware(

    IN ULONG DriveId,

    IN PCHAR LoadOptions

    )

{

    ARC_STATUS Status;

    PCONFIGURATION_COMPONENT_DATA TempFwTree;

    ULONG TempFwHeapUsed;

    extern BOOLEAN FwDescriptorsValid;

    ULONG FileSize;

    ULONG DetectFileId;

    FILE_INFORMATION FileInformation;

    PUCHAR DetectionBuffer;

    PUCHAR Options;

    UCHAR Buffer[100];

    LARGE_INTEGER SeekPosition;

    ULONG Read;

// 檢查在根目錄下是否存在檔案ntdetect.com,如果有的話就将其加載到預定義的位置并将控制權轉交給他

#if defined(ELTORITO)

    if (ElToritoCDBoot) {

        // 假設ntdetect.com在i386目錄下

        Status = BlOpen( DriveId,

                         "\\i386\\ntdetect.com",

                         ArcOpenReadOnly,

                         &DetectFileId );

    } else {

#endif

    Status = BlOpen( DriveId,

                     "\\ntdetect.com",

                     ArcOpenReadOnly,

                     &DetectFileId );

#if defined(ELTORITO)

    }

#endif

    DetectionBuffer = (PUCHAR)DETECTION_LOADED_ADDRESS;

    if (Status != ESUCCESS) {

#if DBG

        BlPrint("Error opening NTDETECT.COM, status = %x\n", Status);

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

// 擷取ntdetect.com檔案資訊

    Status = BlGetFileInformation(DetectFileId, &FileInformation);

    if (Status != ESUCCESS) {

        BlClose(DetectFileId);

#if DBG

        BlPrint("Error getting NTDETECT.COM file information, status = %x\n", Status); //擷取檔案資訊失敗

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

    FileSize = FileInformation.EndingAddress.LowPart;

    if (FileSize == 0) {

        BlClose(DetectFileId);

#if DBG

        BlPrint("Error: size of NTDETECT.COM is zero.\n"); //擷取檔案末尾失敗

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

    SeekPosition.QuadPart = 0;

    Status = BlSeek(DetectFileId,&SeekPosition,SeekAbsolute);

    if (Status != ESUCCESS) {

        BlClose(DetectFileId);

#if DBG

        BlPrint("Error seeking to start of NTDETECT.COM file\n");  //擷取檔案開頭失敗

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

    Status = BlRead(DetectFileId,DetectionBuffer,FileSize,&Read );

    BlClose(DetectFileId);

    if (Status != ESUCCESS) {

#if DBG

        BlPrint("Error reading from NTDETECT.COM\n");  //讀取檔案失敗

        BlPrint("Read %lx bytes\n",Read);

        BlPrint("Press any key to continue\n");

        while (!GET_KEY()) {

        }

#endif

        return(FALSE);

    }

// 必須傳遞小于1MB的NTDETECT指針,是以使用堆棧中的本地存儲

    if (LoadOptions) {

        strcpy(Buffer, LoadOptions);

        Options = Buffer;

    } else {

        Options = NULL;

    }

    DETECT_HARDWARE((ULONG)(TEMPORARY_HEAP_START - 0x10) * PAGE_SIZE,

                    (ULONG)0x10000,         // 堆大小

                    (PVOID)&TempFwTree,

                    (PULONG)&TempFwHeapUsed,

                    (PCHAR)Options,

                    (ULONG)(LoadOptions ? strlen(LoadOptions) : 0)

                    );

    FwConfigurationTree = TempFwTree;

    FwHeapUsed = TempFwHeapUsed;

    FwDescriptorsValid = FALSE;

    return(TRUE);

}

VOID

DoGlobalInitialization(

    IN PBOOT_CONTEXT BootContextRecord

    )

{

    ARC_STATUS Status;

    Status = InitializeMemorySubsystem(BootContextRecord);  //初始化記憶體子系統

    if (Status != ESUCCESS) {

        BlPrint("InitializeMemory failed %lx\n",Status);

        while (1) {

        }

    }

    ExternalServicesTable=BootContextRecord->ExternalServicesTable;

    MachineType = BootContextRecord->MachineType;

    // 此處開啟光标支援

    HW_CURSOR(0,127);

    BlpResourceDirectory = (PUCHAR)(BootContextRecord->ResourceDirectory);

    BlpResourceFileOffset = (PUCHAR)(BootContextRecord->ResourceOffset);

    OsLoaderBase = BootContextRecord->OsLoaderBase;

    OsLoaderExports = BootContextRecord->OsLoaderExports;

    InitializeMemoryDescriptors ();  //初始化記憶體描述符

}

VOID

BlGetActivePartition(

    OUT PUCHAR BootPartitionName

    )

{

    UCHAR SectorBuffer[512];

    UCHAR NameBuffer[80];

    ARC_STATUS Status;

    ULONG FileId;

    ULONG Count;

    int i;

// 嘗試打開所有的分區并将其讀為引導扇區,并與之前使用的引導扇區進行對比.

// 如果相同,則找到,否則嘗試分區1

    i=1;

    do {

        sprintf(NameBuffer, "multi(0)disk(0)rdisk(0)partition(%u)",i);

        Status = ArcOpen(NameBuffer,ArcOpenReadOnly,&FileId);

        if (Status != ESUCCESS) {

            // 周遊所有分區未找到合适的對象,故設定預設值.

            i=1;

            break;

        } else {

            // 讀取前512個位元組

            Status = ArcRead(FileId, SectorBuffer, 512, &Count);

            ArcClose(FileId);

 if (Status==ESUCCESS) {

                // 隻需要比較前36個位元組

                // 跳轉辨別 3 bytes

                // Oem位段 8位元組

                // 參數塊 25位元組

                if (memcmp(SectorBuffer, (PVOID)0x7c00, 36)==0) {

                    // 找到比對對象.

                    break;

                }

            }

        }

        ++i;

    } while ( TRUE );

    sprintf(BootPartitionName, "multi(0)disk(0)rdisk(0)partition(%u)",i);

    return;

}

#if defined(ELTORITO)    

接下來,os loader 從系統分區(即引導分區)的根目錄下讀入boot.ini 檔案。注意,os loader 包含了讀取目前檔案系統的代碼,它能夠讀取NTFS 檔案系統的子目錄。然後,os loader 清除螢幕,并檢查在系統分區的根目錄下是否有一個有效的hiberfil.sys 檔案,如果存在的話,這一次引導過程轉移到休眠系統的恢複過程。是以,os loader 将控制權交給一段能恢複休眠系統的核心代碼。

如果目前這次引導不是休眠恢複,那麼,os loader 解析boot.ini 檔案,如果該檔案中有多個引導選項,則os loader 顯示一個引導選擇菜單;如果boot.ini 檔案中隻包含一個引導選項,那麼,此菜單不顯示,而是立即應用該引導選項。

代碼摘自\ntos\boot\bldr\i386\initx86.c

//負責打開驅動和讀boot.ini檔案

VOID

BlStartup(

    IN PCHAR PartitionName

    )

{

    PUCHAR Argv[10];

    ARC_STATUS Status;

    ULONG BootFileId;

    PCHAR BootFile;

    ULONG Read;

    PCHAR p;

    ULONG i;

    ULONG DriveId;

    ULONG FileSize;

    ULONG Count;

    LARGE_INTEGER SeekPosition;

    PCHAR LoadOptions = NULL;

    BOOLEAN UseTimeOut=TRUE;

    BOOLEAN AlreadyInitialized = FALSE;

    extern BOOLEAN FwDescriptorsValid;

    // 打開引導分區以便加載引導驅動.

    Status = ArcOpen(PartitionName, ArcOpenReadOnly, &DriveId);

    if (Status != ESUCCESS) {

        BlPrint("Couldn't open drive %s\n",PartitionName);

        BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName);

        goto BootFailed;

    }

    // 初始化雙位元組内碼系統以及顯示支援.

    TextGrInitialize(DriveId);

    do {

        Status = BlOpen( DriveId,

                         "\\boot.ini",

                         ArcOpenReadOnly,

                         &BootFileId );  //此處開始打開boot.ini

        BootFile = MyBuffer;

        RtlZeroMemory(MyBuffer, SECTOR_SIZE+32);  

        if (Status != ESUCCESS) {

            BootFile[0]='\0';

        } else {

 // 通過從頭到尾讀取boot.ini檔案擷取大小

            FileSize = 0;

            do {

                Status = BlRead(BootFileId, BootFile, SECTOR_SIZE, &Count);

                if (Status != ESUCCESS) {

                    BlClose(BootFileId);

                    BlPrint(BlFindMessage(BL_READ_ERROR),Status);

                    BootFile[0] = '\0';   //結束符

                    FileSize = 0;

                    Count = 0;

                    goto BootFailed;

                }

                FileSize += Count;

            } while (Count != 0);

            if (FileSize >= SECTOR_SIZE) {

                // 如果boot.ini檔案大于一個扇區的大小,那麼就需要重新配置設定更大的緩沖區

                BootFile=FwAllocateHeap(FileSize);

            }

            if (BootFile == NULL) {

                BlPrint(BlFindMessage(BL_READ_ERROR),ENOMEM);

                BootFile = MyBuffer;

                BootFile[0] = '\0';

                goto BootFailed;

            } else {

                SeekPosition.QuadPart = 0;

                Status = BlSeek(BootFileId,

                                &SeekPosition,

                                SeekAbsolute);

                if (Status != ESUCCESS) {

                    BlPrint(BlFindMessage(BL_READ_ERROR),Status);

                    BootFile = MyBuffer;

                    BootFile[0] = '\0';

                    goto BootFailed;

                } else {

                    Status = BlRead( BootFileId,

                                     BootFile,

                                     FileSize,

                                     &Read );

                    SeekPosition.QuadPart = 0;

                    Status = BlSeek(BootFileId,

                                    &SeekPosition,

                                    SeekAbsolute);

                    if (Status != ESUCCESS) {

                        BlPrint(BlFindMessage(BL_READ_ERROR),Status);

                        BootFile = MyBuffer;

                        BootFile[0] = '\0';

                        goto BootFailed;

                    } else {

                        BootFile[Read]='\0';

                    }

                }

            }

            // 搜尋Ctrl-Z

            p=BootFile;

            while ((*p!='\0') && (*p!=26)) {

                ++p;

            }

            if (*p != '\0') {

                *p='\0';

            }

            BlClose(BootFileId);

        }

        if (!AlreadyInitialized) {

            AbiosInitDataStructures();

        }

        MdShutoffFloppy();  //關閉軟驅

        TextClearDisplay();   //清除顯示文本

        p=BlSelectKernel(DriveId,BootFile, &LoadOptions, UseTimeOut);

        if (!AlreadyInitialized) {

            BlPrint(BlFindMessage(BL_NTDETECT_MSG));

            if (!BlDetectHardware(DriveId, LoadOptions)) {

                BlPrint(BlFindMessage(BL_NTDETECT_FAILURE));

                return;

            }

            TextClearDisplay();

            // 初始化SCSI引導驅動

            if(!_strnicmp(p,"scsi(",5)) {

                AEInitializeIo(DriveId);

            }

            ArcClose(DriveId);

            // 設定标志位表示記憶體描述符不能被改變.

            FwDescriptorsValid = FALSE;

        } else {

            TextClearDisplay();

        }

        //設定該标志位用于表示ntdetect和abios的初始化例程已經運作

        AlreadyInitialized = TRUE;

        //設定引導菜單無等待操作的時間,即使用者如不操作則一直停留

        UseTimeOut=FALSE;

        i=0;

        while (*p !='\\') {

            KernelBootDevice[i] = *p;

            i++;

            p++;

        }

        KernelBootDevice[i] = '\0';

        strcpy(OsLoadFilename,"osloadfilename=");

        strcat(OsLoadFilename,p);

        // 這裡隻能使用參數”osloader= variable ”來指定從哪裡加載HAL.DLL.  

// 因為x86系統未指定”系統分區 ” 是以将從路徑\nt\system\HAL.DLL加載HAL.DLL

        strcpy(OsLoaderFilename,"osloader=");

        strcat(OsLoaderFilename,p);

        strcat(OsLoaderFilename,"\\System32\\NTLDR");

        strcpy(SystemPartition,"systempartition=");

        strcat(SystemPartition,KernelBootDevice);

        strcpy(OsLoadPartition,"osloadpartition=");

        strcat(OsLoadPartition,KernelBootDevice);

        strcpy(OsLoadOptions,"osloadoptions=");

        if (LoadOptions) {

            strcat(OsLoadOptions,LoadOptions);

        }

        strcpy(ConsoleInputName,"consolein=multi(0)key(0)keyboard(0)");

        strcpy(ConsoleOutputName,"consoleout=multi(0)video(0)monitor(0)");

        strcpy(X86SystemPartition,"x86systempartition=");

        strcat(X86SystemPartition,PartitionName);

        Argv[0]="load";

        Argv[1]=OsLoaderFilename;

        Argv[2]=SystemPartition;

        Argv[3]=OsLoadFilename;

        Argv[4]=OsLoadPartition;

        Argv[5]=OsLoadOptions;

        Argv[6]=ConsoleInputName;

        Argv[7]=ConsoleOutputName;

        Argv[8]=X86SystemPartition;

        Status = BlOsLoader(9,Argv,NULL);

    BootFailed:

        if (Status != ESUCCESS) {

            // 引導失敗就重新啟動

            while (TRUE) {

                GET_KEY();

            }

        } else {

            //重新打開裝置

            Status = ArcOpen(BootPartitionName, ArcOpenReadOnly, &DriveId);

            if (Status != ESUCCESS) {

                BlPrint(BlFindMessage(BL_DRIVE_ERROR),BootPartitionName);

                goto BootFailed;

            }

        }

    } while (TRUE);

}

然後,os loader 加載核心子產品映像檔案,預設為ntoskrnl.exe,以及HAL 映像檔案,預設為hal.dll。再加載系統資料庫的SYSTEM 儲巢,即\WINDOWS\system32\config\system 檔案。通過檢查SYSTEM 儲巢中的設定資訊,它可以知道哪些裝置驅動程式必須被加載進來,即被标記為“引導-啟動”(SERVICE_BOOT_START)的裝置驅動程式。然後它加載所有這些被标記為“引導-啟動”的裝置驅動程式,以及通路系統目錄所必需的檔案系統驅動程式。注意,在此之前os loader 也可以通路系統分區,但并非通過檔案系統驅動程式。

代碼摘自\ntos\boot\bldr\osloader.c

// 定義擴充靜态資料

ULONG BlConsoleOutDeviceId = 0;

ULONG BlConsoleInDeviceId = 0;

ULONG BlDcacheFillSize = 32;

#if DBG

BOOLEAN BlOutputDots=FALSE;

#else

BOOLEAN BlOutputDots=TRUE;

#endif

CHAR KernelFileName[8+1+3+1]="ntoskrnl.exe";

CHAR HalFileName[8+1+3+1]="hal.dll";

//此處定義名為"ntoskrnl.exe"和"hal.dll"的檔案

ARC_STATUS

BlOsLoader (

    IN ULONG Argc,

    IN PCHAR Argv[],

    IN PCHAR Envp[]

    )

{

    CHAR BootDirectoryPath[256];

    ULONG CacheLineSize;

    PCHAR ConsoleOutDevice;

    PCHAR ConsoleInDevice;

    ULONG Count;

    PCONFIGURATION_COMPONENT_DATA DataCache;

    CHAR DeviceName[256];

    CHAR DevicePrefix[256];

    PCHAR DirectoryEnd;

    CHAR DllName[256];

    CHAR DriverDirectoryPath[256];

    PCHAR FileName;

    ULONG FileSize;

    PLDR_DATA_TABLE_ENTRY HalDataTableEntry;

    CHAR HalDirectoryPath[256];

    CHAR KernelDirectoryPath[256];

    PVOID HalBase;

    PVOID SystemBase;

    ULONG Index;

    ULONG Limit;

    ULONG LinesPerBlock;

    PCHAR LoadDevice;

    ULONG LoadDeviceId;

    PCHAR LoadFileName;

    PCHAR LoadOptions;

    ULONG i;

    CHAR OutputBuffer[256];

    ARC_STATUS Status;

    PLDR_DATA_TABLE_ENTRY SystemDataTableEntry;

    PCHAR SystemDevice;

    ULONG SystemDeviceId;

    PTRANSFER_ROUTINE SystemEntry;

    PIMAGE_NT_HEADERS NtHeaders;

    PWSTR BootFileSystem;

    PCHAR LastKnownGood;

    BOOLEAN BreakInKey;

    CHAR BadFileName[128];

    PBOOTFS_INFO FsInfo;

// 擷取控制台輸出裝置的名字并擷取寫入權限

    ConsoleOutDevice = BlGetArgumentValue(Argc, Argv, "consoleout");

    if (ConsoleOutDevice == NULL) {

        return ENODEV;

    }

    Status = ArcOpen(ConsoleOutDevice, ArcOpenWriteOnly, &BlConsoleOutDeviceId);

    if (Status != ESUCCESS) {

        return Status;

    }

// 擷取控制台輸入裝置的名字并擷取讀取權限

    ConsoleInDevice = BlGetArgumentValue(Argc, Argv, "consolein");

    if (ConsoleInDevice == NULL) {

        return ENODEV;

    }

    Status = ArcOpen(ConsoleInDevice, ArcOpenReadOnly, &BlConsoleInDeviceId);

    if (Status != ESUCCESS) {

        return Status;

    }

    // 聲明OS Loader.

    strcpy(&OutputBuffer[0], "OS Loader V4.00\r\n");

    ArcWrite(BlConsoleOutDeviceId, &OutputBuffer[0],

             strlen(&OutputBuffer[0]),

             &Count);

// 初始化記憶體描述符清單,OSloader系統堆和參數塊

    Status = BlMemoryInitialize();

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_MEM_CLASS,

                          DIAG_BL_MEMORY_INIT,

                          LOAD_HW_MEM_ACT);

        goto LoadFailed;

    }

// 計算資料緩存大小.該值用來對齊I/O緩沖區以防主機系統不支援連續的緩存

    DataCache = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot,

                                         CacheClass,

                                         SecondaryCache,

                                         NULL);

    if (DataCache == NULL) {

        DataCache = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot,

                                             CacheClass,

                                             SecondaryDcache,

                                             NULL);

        if (DataCache == NULL) {

            DataCache = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot,

                                                 CacheClass,

                                                 PrimaryDcache,

                                                 NULL);

        }

    }

    if (DataCache != NULL) {

        LinesPerBlock = DataCache->ComponentEntry.Key >> 24;

        CacheLineSize = 1 << ((DataCache->ComponentEntry.Key >> 16) & 0xff);

        BlDcacheFillSize = LinesPerBlock * CacheLineSize;

    }

    // 初始化OS loader的I/O系統

    Status = BlIoInitialize();

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_DISK_CLASS,

                          DIAG_BL_IO_INIT,

                          LOAD_HW_DISK_ACT);

        goto LoadFailed;

    }

// 初始化資源節

    Status = BlInitResources(Argv[0]);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_DISK_CLASS,

                          DIAG_BL_IO_INIT,

                          LOAD_HW_DISK_ACT);

    }

// 初始化NT配置樹.

    BlLoaderBlock->ConfigurationRoot = NULL;

    Status = BlConfigurationInitialize(NULL, NULL);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                     DIAG_BL_CONFIG_INIT,

                     LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    // 複制osloadoptions參數給LoaderBlock

    LoadOptions = BlGetArgumentValue(Argc, Argv, "osloadoptions");

    if (LoadOptions != NULL) {

        FileSize = strlen(LoadOptions)+1;

        FileName = (PCHAR)BlAllocateHeap(FileSize);

        strcpy(FileName, LoadOptions);

        BlLoaderBlock->LoadOptions = FileName;

        //檢測标志值判斷是否應該輸出檔案名而不是單獨的原點

        if ((strstr(FileName,"SOS")!=NULL) ||

            (strstr(FileName,"sos")!=NULL)) {

            BlOutputDots=FALSE;

        }

        FileName=strstr(BlLoaderBlock->LoadOptions,"HAL=");

        if (FileName) {

            for (i=0; i<sizeof(HalFileName); i++) {

                if (FileName[4+i]==' ') {

                    HalFileName[i]='\0';

                    break;

                }

                HalFileName[i]=FileName[4+i];

            }

        }

        HalFileName[sizeof(HalFileName)-1]='\0';

        FileName=strstr(BlLoaderBlock->LoadOptions,"KERNEL=");

        if (FileName) {

            for (i=0; i<sizeof(KernelFileName); i++) {

                if (FileName[7+i]==' ') {

                    KernelFileName[i]='\0';

                    break;

                }

                KernelFileName[i]=FileName[7+i];

            }

        }

        KernelFileName[sizeof(KernelFileName)-1]='\0';

    } else {

        BlLoaderBlock->LoadOptions = NULL;

    }

    // 擷取加載裝置的名稱以及讀取的權限.

    LoadDevice = BlGetArgumentValue(Argc, Argv, "osloadpartition");

    if (LoadDevice == NULL) {

             Status = ENODEV;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_GET_BOOT_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    Status = ArcOpen(LoadDevice, ArcOpenReadOnly, &LoadDeviceId);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_DISK_CLASS,

                          DIAG_BL_OPEN_BOOT_DEVICE,

                          LOAD_HW_DISK_ACT);

        goto LoadFailed;

    }

    // 擷取系統裝置的名稱以及讀取的權限.

    SystemDevice = BlGetArgumentValue(Argc, Argv, "systempartition");

    if (SystemDevice == NULL) {

        Status = ENODEV;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_GET_SYSTEM_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    Status = ArcOpen(SystemDevice, ArcOpenReadOnly, &SystemDeviceId);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_OPEN_SYSTEM_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    // 初始化調試系統

    BlLogInitialize(SystemDeviceId);

//顯示系統提示符,給使用者更多的準備時間

    BlStartConfigPrompt();

#if defined(_PPC_)

    Status = BlPpcInitialize();

    if (Status != ESUCCESS) {

        goto LoadFailed;

    }

#endif // defined(_PPC_)

    // 擷取系統根目錄的路徑名稱.

    LoadFileName = BlGetArgumentValue(Argc, Argv, "osloadfilename");

    if (LoadFileName == NULL) {

        Status = ENOENT;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FW_GET_BOOT_DEVICE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

// 生成SYSTEM32目錄名稱 

 strcpy(BootDirectoryPath, LoadFileName);

    strcat(BootDirectoryPath, "\\System32\\");

    // 生成ntoskrnl.exe的全路徑

    //      "\winnt\system32\ntoskrnl.exe"

    strcpy(KernelDirectoryPath, BootDirectoryPath);

    strcat(KernelDirectoryPath, KernelFileName);

    // 加載核心子產品映像檔案.

    BlOutputLoadMessage(LoadDevice, KernelDirectoryPath);

    Status = BlLoadImage(LoadDeviceId,

                         LoaderSystemCode,

                         KernelDirectoryPath,

                         TARGET_IMAGE,

                         &SystemBase);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                                         DIAG_BL_LOAD_SYSTEM_IMAGE,

                                         LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

// 無論檔案系統是什麼,都需要與引導驅動一起加載

    FsInfo = BlGetFsInfo(LoadDeviceId);

    if (FsInfo != NULL) {

        BootFileSystem = FsInfo->DriverName;

    } else {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                                         DIAG_BL_LOAD_SYSTEM_IMAGE,

                                         LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

// 擷取OS loader檔案的路徑名稱,并除去目錄名稱,用來加載HAL.DLL

    FileName = BlGetArgumentValue(Argc, Argv, "osloader");

    if (FileName == NULL) {

        Status = ENOENT;

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_FIND_HAL_IMAGE,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    DirectoryEnd = strrchr(FileName, '\\');

    FileName = strchr(FileName, '\\');

    HalDirectoryPath[0] = 0;

    if (DirectoryEnd != NULL) {

        Limit = (ULONG)DirectoryEnd - (ULONG)FileName + 1;

        for (Index = 0; Index < Limit; Index += 1) {

            HalDirectoryPath[Index] = *FileName++;

        }

        HalDirectoryPath[Index] = 0;

    }

    // 生成完整的路徑名稱并将HAL加載入系統記憶體中去.

    strcpy(&DllName[0], &HalDirectoryPath[0]);

    strcat(&DllName[0], HalFileName);

    BlOutputLoadMessage(SystemDevice, &DllName[0]);

    Status = BlLoadImage(SystemDeviceId,

                         LoaderHalCode,

                         &DllName[0],

                         TARGET_IMAGE,

                         &HalBase);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                          DIAG_BL_LOAD_HAL_IMAGE,

                          LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

    // 為ntoskrnl.exe生成資料表入口.

    Status = BlAllocateDataTableEntry("ntoskrnl.exe",

                                      KernelDirectoryPath,

                                      SystemBase,

                                      &SystemDataTableEntry);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_SYSTEM_IMAGE,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

    // 為hal.dll生成資料表入口.

    Status = BlAllocateDataTableEntry("hal.dll",

                                      &DllName[0],

                                      HalBase,

                                      &HalDataTableEntry);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_HAL_IMAGE,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

#if defined(_ALPHA_)

    Status = BlLoadPal(SystemDeviceId,

                       LoaderSystemCode,

                       &HalDirectoryPath[0],

                       TARGET_IMAGE,

                       &BlLoaderBlock->u.Alpha.PalBaseAddress,

                       SystemDevice);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_MIS_FILE_CLASS,

                          DIAG_BL_LOAD_SYSTEM_DLLS,

                          LOAD_SW_FILE_REINST_ACT);

        goto LoadFailed;

    }

#endif // _ALPHA_

// 掃描系統映像的導入表以及加載相關的dll

    Status = BlScanImportDescriptorTable(LoadDeviceId,

                                         LoadDevice,

                                         &BootDirectoryPath[0],

                                         SystemDataTableEntry);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_SYSTEM_DLLS,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

// 掃描HAL.dll的導入表以及加載相關的dll

    Status = BlScanImportDescriptorTable(SystemDeviceId,

                                         SystemDevice,

                                         &HalDirectoryPath[0],

                                         HalDataTableEntry);

 if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,

                          DIAG_BL_LOAD_HAL_DLLS,

                          LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

// 重定位系統入口點并設定系統指定的資訊

    NtHeaders = RtlImageNtHeader(SystemBase);

    SystemEntry = (PTRANSFER_ROUTINE)((ULONG)SystemBase +

                                NtHeaders->OptionalHeader.AddressOfEntryPoint);

#ifdef MIPS

    BlLoaderBlock->u.Mips.GpBase = (ULONG)SystemBase +

        NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress;

#endif

#if defined(_ALPHA_)

    BlLoaderBlock->u.Alpha.GpBase = (ULONG)SystemBase +

        NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress;

#endif

// 複制所有的裝置驅動的目錄路徑

    strcpy(&DriverDirectoryPath[0], &BootDirectoryPath[0]);

    strcat(&DriverDirectoryPath[0], "\\Drivers\\");

    // 為NLS資料配置設定結構體.  這将通過BlLoadAndScanSystemHive函數填充并加載.

    BlLoaderBlock->NlsData = BlAllocateHeap(sizeof(NLS_DATA_BLOCK));

    if (BlLoaderBlock->NlsData == NULL) {

        Status = ENOMEM;

        BlFatalError(LOAD_HW_MEM_CLASS,

                          DIAG_BL_LOAD_SYSTEM_HIVE,

                          LOAD_HW_MEM_ACT);

        goto LoadFailed;

}

// 加載系統資料庫的SYSTEM儲巢

    Status = BlLoadAndScanSystemHive(LoadDeviceId,

                                     LoadDevice,

                                     LoadFileName,

                                     BootFileSystem,

                                     BadFileName);

    if (Status != ESUCCESS) {

        if (BlRebootSystem) {

            Status = ESUCCESS;

        } else {

            BlBadFileMessage(BadFileName);

        }

        goto LoadFailed;

    }

    // 生成符合ARC命名規範的引導裝置名稱以及NT路徑名.

    Status = BlGenerateDeviceNames(LoadDevice, &DeviceName[0], &DevicePrefix[0]);

    if (Status != ESUCCESS) {

             BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_ARC_BOOT_DEV_NAME,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

    FileSize = strlen(&DeviceName[0]) + 1;

    FileName = (PCHAR)BlAllocateHeap(FileSize);

    strcpy(FileName, &DeviceName[0]);

    BlLoaderBlock->ArcBootDeviceName = FileName;

    FileSize = strlen(LoadFileName) + 2;

    FileName = (PCHAR)BlAllocateHeap( FileSize);

    strcpy(FileName, LoadFileName);

    strcat(FileName, "\\");

BlLoaderBlock->NtBootPathName = FileName;

// 生成符合ARC命名規範的HAL裝置名稱以及NT路徑名.

#ifdef i386

    strcpy(&DeviceName[0], BlGetArgumentValue(Argc, Argv, "x86systempartition"));

#else

    Status = BlGenerateDeviceNames(SystemDevice, &DeviceName[0], &DevicePrefix[0]);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_HW_FW_CFG_CLASS,

                          DIAG_BL_ARC_BOOT_DEV_NAME,

                          LOAD_HW_FW_CFG_ACT);

        goto LoadFailed;

    }

#endif //i386

    FileSize = strlen(&DeviceName[0]) + 1;

    FileName = (PCHAR)BlAllocateHeap(FileSize);

    strcpy(FileName, &DeviceName[0]);

    BlLoaderBlock->ArcHalDeviceName = FileName;

#ifdef i386

    //此處寫死osloader的位址

    FileName = (PCHAR)BlAllocateHeap(2);

    FileName[0] = '\\';

    FileName[1] = '\0';

#else

    FileSize = strlen(&HalDirectoryPath[0]) + 1;

    FileName = (PCHAR)BlAllocateHeap(FileSize);

    strcpy(FileName, &HalDirectoryPath[0]);

#endif //i386

BlLoaderBlock->NtHalPathName = FileName;

// 擷取NTFS裝置的簽名資訊以允許核心建立正确的ARC名稱

    BlGetArcDiskInformation();

    // 此處執行特定的安裝代碼.

    Status = BlSetupForNt(BlLoaderBlock);

    if (Status != ESUCCESS) {

        BlFatalError(LOAD_SW_INT_ERR_CLASS,DIAG_BL_SETUP_FOR_NT,LOAD_SW_INT_ERR_ACT);

        goto LoadFailed;

    }

// 關閉調試系統

    BlLogTerminate();

// 将控制權轉換給已加載的映像

    (SystemEntry)(BlLoaderBlock);

    Status = EBADF;

    BlFatalError(LOAD_SW_BAD_FILE_CLASS,DIAG_BL_KERNEL_INIT_XFER,LOAD_SW_FILE_REINST_ACT);

LoadFailed:

return Status;

}

至此,引導系統所需的子產品(包括核心映像、HAL,以及必要的裝置驅動程式)都已經被加載到記憶體中。而且,在此過程中os loader 也已經構造了一個參數塊,記錄下了這次引導過程中加載器所獲得的各種參數資訊。參數塊的類型為LOADER_PARAMETER_BLOCK,Windows 核心在初始化過程中将會用到這些參數資訊。WRK 中包含有它的定義,如下(見public\internal\base\inc\arc.h 檔案):

typedef struct _LOADER_PARAMETER_BLOCK {

    LIST_ENTRY LoadOrderListHead; //加載的子產品連結清單,每個元素都為KLDR_DATA_TABLE_ENTRY

LIST_ENTRY MemoryDescriptorListHead;

//記憶體描述符連結清單,每個元素都為MEMORY_ALLOCATION_DESCRIPTOR    

    LIST_ENTRY BootDriverListHead; //引導驅動程式連結清單,每個元素都為BOOT_DRIVER_LIST_ENTRY

    ULONG_PTR KernelStack; //核心棧頂

    ULONG_PTR Prcb; //程序環境,指向一個程序控制塊

    ULONG_PTR Process; //初始程序,EPROCESS

    ULONG_PTR Thread; //初始線程,ETHREAD

    ULONG RegistryLength; //系統儲巢的長度

    PVOID RegistryBase; //系統儲巢的基位址

PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;

//配置樹,包含ISA,磁盤和ACPI的配置資料

    PCHAR ArcBootDeviceName; //引導分區的ARC名稱

    PCHAR ArcHalDeviceName;                //系統分區的ARC名稱

    PCHAR NtBootPathName;                  //OS目錄的路徑名稱,比如"\Windows"

    PCHAR NtHalPathName; //OD加載器的路徑名稱,比如"\"

    PCHAR LoadOptions; //引導選項,來自boot.ini

    PNLS_DATA_BLOCK NlsData; //包含ANSI代碼頁,OEM代碼頁和Unicode碼表

    PARC_DISK_INFORMATION ArcDiskInformation;    //所有磁盤的簽名結構

    PVOID OemFontFile; //OEM字型檔案

    struct _SETUP_LOADER_BLOCK *SetupLoaderBlock;  //網絡引導或文字模式安裝引導

    PLOADER_PARAMETER_EXTENSION Extension;      //擴充部分

union {

        I386_LOADER_BLOCK I386;

        // ALPHA_LOADER_BLOCK Alpha;

        // IA64_LOADER_BLOCK Ia64;

} u;

} LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;

由上述代碼的注解可以看出,LOADER_PARAMETER_BLOCK 參數塊中包含了有關這次引導的各種參數資訊和系統配置,這裡ARC 名稱是指符合ARC(Advanced RISCComputing)命名規範的字元串,例如“multi(0)disk(0)rdisk(0)partition(1)”是指0 号磁盤控制器第一塊硬碟上的第一個分區。注意,參數塊中的絕大多數資訊由os loader 來填充,而在接下來的核心初始化過程中使用,但也有例外,比如有關線程和程序的資訊需要在核心初始化過程中填充。

最後,os loader 将控制權交給核心子產品的入口函數,該函數将不再傳回,是以,接下來的引導過程由核心子產品繼續進行,引導扇區和系統加載器(ntldr 或os loader)的使命已經完成。下圖顯示了以上介紹的引導步驟。

我們已經看到,ntldr 構造了一個類型為LOADER_PARAMETER_BLOCK 的參數塊,把與系統初始化相關的參數資訊包裝到此結構中,然後将控制權傳遞給核心子產品ntoskrnl.exe 的入口函數。是以,核心的初始化從核心子產品的入口函數開始,WRK 包含了核心初始化過程的絕大多數代碼。此入口函數為KiSystemStartup,它是一個彙編函數,位于base\ntos\ke\i386\newsysbg.asm 檔案中。

cPublicProc _KiSystemStartup        ,1

        push    ebp

        mov     ebp, esp

        sub     esp, 32                       ;配置設定空間給全局變量

        mov     ebx, dword ptr KissLoaderBlock

        mov     _KeLoaderBlock, ebx         ; 擷取加載器的參數

        movzx   ecx, _KeNumberProcessors    ;擷取處理器的個數

        mov     KissPbNumber, ecx

        or      ecx, ecx                       ;判斷是否為引導處理器

        jnz     @f                            ;不是引導處理器

        ; 初始化0階段使用靜态的記憶體

        mov     dword ptr [ebx].LpbThread,      offset _KiInitialThread ;初始化線程

        mov     dword ptr [ebx].LpbKernelStack, offset P0BootStack     ;核心堆棧

        push    KGDT_R0_PCR                 ; P0 needs FS set

        pop     fs

        ; 在Prcb中存儲處理器序号

        mov     byte ptr PCR[PcPrcbData+PbNumber], cl   

;此處開始構造PCR (Processor Control Region)

@@:

        mov     eax, dword ptr [ebx].LpbThread

        mov     dword ptr KissIdleThread, eax

        lea     ecx, [eax].ThApcState.AsApcListHead  ;初始化核心APC連結清單頭

        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    

        jne     kiss_notp0                    

; 初始化GDT,PCR,TSS,IDT

        stdCall   GetMachineBootPointers

;   (edi) -> gdt

;   (esi) -> pcr

;   (edx) -> tss

;   (eax) -> idt

; 儲存相關參數到相應的寄存器

        mov     KissGdt, edi

        mov     KissPcr, esi

        mov     KissTss, edx

        mov     KissIdt, eax

;将TSS轉換為32位,因為ntloader傳遞的tss為16位

        lea     ecx,[edi]+KGDT_TSS      ; (ecx) -> TSS descriptor

        mov     byte ptr [ecx+5],089h   ; 32bit, dpl=0, present, TSS32, not busy

; KiInitializeTSS2(

;       TSS的線性位址

;       TSS描述符的線性位址

;       );

        stdCall   _KiInitializeTSS2, <KissTss, ecx>

        stdCall   _KiInitializeTSS, <KissTss>

        mov     cx,KGDT_TSS

        ltr     cx   ;從GDT中取出相應的TSS段描述符

;   設定32位雙重故障任務門去擷取雙重故障異常

        mov     eax,KissIdt

        lea     ecx,[eax]+40h            ; 異常向量号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   ; 32位, 描述符特權級别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(

;       雙重故障任務狀态段

;       );

        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    ; 設定CS的值

        mov     word ptr [edx+058h],KGDT_R0_PCR      ; 設定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

;  設定32位不可屏蔽中斷任務門去擷取不可屏蔽中斷異常

        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(

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

; 在目前線程對象中設定目前程序指針

        mov     edx, KissIdleThread

        mov     ecx, offset FLAT:_KiInitialProcess ; (ecx)-> idle process obj

        mov     [edx]+ThApcState+AsProcess, ecx ; set addr of thread's process

; 設定 PCR: Teb, Prcb 指針.  

;  Prcb 相關參數将在函數 _KiInitializeKernel中設定

        mov     dword ptr PCR[PcTeb], 0   ; PCR->Teb = 0

; 初始化KernelDr7和KernelDr6為0.  該操作必須在調試器調用前完成.

mov     dword ptr PCR[PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr6],0

mov     dword ptr PCR[PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr7],0

; 核心IDT重新設定,轉換成i386可以識别的次序.該操作隻能由引導處理器完成

        stdCall   _KiSwapIDT                  

        mov     eax,KGDT_R3_DATA OR RPL_MASK    ; 設定請求特權級RPL為ring 3

        mov     ds,ax

        mov     es,ax

;  複制陷阱處理器替換核心調試處理器

        mov     eax, KissIdt              ; (eax)-> Idt

        push    dword ptr [eax+40h]     ; 儲存雙重故障描述符

        push    dword ptr [eax+44h]

        push    dword ptr [eax+10h]     ; 儲存不可屏蔽中斷故障描述符

        push    dword ptr [eax+14h]

        mov     edi,KissIdt

        mov     esi,offset FLAT:_IDT

        mov     ecx,offset FLAT:_IDTLEN ; 

        shr     ecx,2

        rep     movsd

        pop     dword ptr [eax+14h]     ; 恢複雙重故障描述符        

pop     dword ptr [eax+10h]

        pop     dword ptr [eax+44h]     ; 恢複不可屏蔽中斷故障描述符

        pop     dword ptr [eax+40h]

ifdef QLOCK_STAT_GATHER

        EXTRNP  KiCaptureQueuedSpinlockRoutines,0,,FASTCALL

        fstCall KiCaptureQueuedSpinlockRoutines

endif

kiss_notp0:

ifndef NT_UP

; 告知引導處理器該處理器已經開始運作.

        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

; 添加目前處理器到活動清單,并更新BroadcastMasks

        mov     ecx, dword ptr KissPbNumber ; 标記該處理器為活動的

        mov     byte ptr PCR[PcNumber], cl

        mov     eax, 1

        shl     eax, cl                         ; 關聯字段

        mov     PCR[PcSetMember], eax

        mov     PCR[PcSetMemberCopy], eax

        mov     PCR[PcPrcbData.PbSetMember], eax

; 初始化處理器間的中斷向量表并自增就緒處理器計數值以開啟核心調試器.

        stdCall   _HalInitializeProcessor, <dword ptr KissPbNumber, KissLoaderBlock> 

//為目前處理器初始化其HAL中的PCR和處理器間中斷向量

ifdef _APIC_TPR_

; 通過hal記錄IRQL表,并傳遞過來

        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    ; 活動處理器的新關聯值

; 初始化ABIOS資料結構.

;  KiInitializeAbios例程必須在KeLoaderBlock初始化完成之後調用

        stdCall   _KiInitializeAbios, <dword ptr KissPbNumber>

        inc     _KeNumberProcessors         ; 又有新的處理器被激活

        xor     eax, eax                       ; 釋放執行鎖

        mov     _KiFreezeExecutionLock, eax

        cmp     byte ptr KissPbNumber, 0

        jnz     @f

; 不能在調試器中暫停

        stdCall   _KdInitSystem, <0,_KeLoaderBlock>

if  DEVL

; 給予調試器獲得控制權的機會.

        POLL_DEBUGGER

endif   ; DEVL

@@:

        nop                             ; 留一個位置給int3指令

; 設定初始化的IRQL = HIGH_LEVEL

        RaiseIrql HIGH_LEVEL

        mov     KissIrql, al

        or      _KiBootFeatureBits, KF_CMPXCHG8B ; CMPXCHG8B是XP的一個辨別

; 初始化ebp,esp和其他參數寄存器,為初始化核心做準備

        mov     ebx, KissIdleThread

        mov     edx, KissIdleStack

        mov     eax, KissPbNumber

        and     edx, NOT 3h             ; 4位元組邊界對齊

        xor     ebp, ebp                ; (ebp) = 0.   沒有更多的棧幀了

        mov     esp, edx

; 為空閑線程棧NPX_SAVE_AREA預留白間并初始化

       sub    esp, NPX_FRAME_LENGTH+KTRAP_FRAME_LENGTH+KTRAP_FRAME_ALIGN

       push    CR0_EM+CR0_TS+CR0_MP    ; 為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

; 初始化系統資料結構和HAL

stdCall    _KiInitializeKernel,<offset _KiInitialProcess,ebx,edx,dword ptr PCR[PcPrcb],eax,_KeLoaderBlock>

//執行核心初始化

; 設定空閑線程的優先級.

        mov     ebx,PCR[PcPrcbData+PbCurrentThread] ; 設定空閑線程的位址

        mov     byte ptr [ebx]+ThPriority, 0 ; 設定優先級為0

; 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   

// KiInitializeKernel函數傳回以後,啟動中斷,将IRQL降低為DISPATCH_LEVEL

// 進而允許線程排程器選擇新的線程   

        mov     ebx, PCR[PcSelfPcr]     ; 擷取PCR的位址

; 在一個多處理器系統中,引導處理器直接進入空閑循環.而其他的處理器不會直接進入空閑循環;

; 而是等到所有處理器已經開啟并且引導主扇區允許進入為止;

; 屏障KiBarrierWait對于系統的第一個處理器并不起作用,而僅對後續的處理器起作用

ifndef NT_UP

@@:     cmp     _KiBarrierWait, 0       ; 判斷是否設定了KiBarrierWait

        YIELD

        jnz     short @b              

endif

        push    0                       

        jmp     @[email protected]           ; 進入空閑循環

stdENDP _KiSystemStartup