天天看点

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