In describing the Windows 2000 boot process, we'll start with the installation of Windows 2000 and proceed through the execution of Ntldr and Ntdetect. Device drivers are a crucial part of the boot process, so we'll explain the way that they control the point in the boot process at which they load and initialize. Then we'll describe how the executive subsystems initialize and how the kernel launches the user-mode portion of Windows 2000 by starting the Session Manager process (Smss.exe), the Win32 subsystem, and the logon process (Winlogon). Along the way, we'll highlight the points at which various text appears on the screen to help you correlate the internal process with what you see when you watch Windows 2000 boot. Table 4-1 presents a summary of boot-process components with their execution modes and responsibilities.
Table 4-1 Boot Process Components
|Master boot record (MBR) code||16-bit real mode||Reads and loads partition boot sectors|
|Boot sector||16-bit real mode||Reads the root directory to load Ntldr|
|Ntldr||16-bit real mode and 32-bit protected mode;turns on paging||Reads Boot.ini, presents boot menu, and loads Ntoskrnl.exe, Bootvid.dll, Hal.dll, and boot-start device drivers|
|Ntoskrnl.exe||32-bit protected mode with paging||Initializes executive subsystems and boot and system-start device drivers, prepares the system for running native applications, and runs Smss.exe|
|Smss||32-bit native application||Loads Win32 subsystem, including Win32k.sys and Csrss.exe, and starts Winlogon process|
|Winlogon||32-bit native application||Starts the service control manager (SCM), the Local Security Subsystem (Lsass), and presents interactive logon dialog box|
|Service control manager (SCM)||32-bit native application||Loads and initializes auto-start device drivers and Win32 services|
The Windows 2000 boot process doesn't begin when you power on your computer or press the reset button. It begins when you install Windows 2000 on your computer. At some point during the execution of the Windows 2000 Setup program, the system's primary hard disk is prepared with code that takes part in the boot process. Before we get into what this code does, let's look at how and where Windows 2000 places the code on a disk. Since the early days of MSDOS, a standard has existed on x86 systems for the way physical hard disks are divided into volumes. Microsoft operating systems split hard disks into discrete areas known as partitions and use file systems (such as FAT and NTFS) to format each partition into a volume. A hard disk can contain up to four primary partitions. Because this apportioning scheme would limit a disk to four volumes, a special partition type, called an extended partition, further allocates up to four additional partitions within each primary partition. Extended partitions can contain extended partitions, which can contain extended partitions, and so on, making the number of volumes an operating system can place on a disk effectively infinite. Figure 4-1 shows an example of a hard disk layout. (You can learn more about Windows 2000 partitioning in Chapter 10, which covers storage management.)
Figure 4-1 Example hard disk layout
Physical disks are addressed in units known as sectors. A hard disk sector on an IBM-compatible PC is typically 512 bytes. Utilities that prepare hard disks for the definition of logical drives, including the MS-DOS Fdisk utility or the Windows 2000 Setup program, write a sector of data called a master boot record (MBR) to the first sector on a hard disk. The MBR includes a fixed amount of space that contains executable instructions (called boot code) and a table (called a partition table) with four entries that define the locations of the primary partitions on the disk. When an IBM-compatible computer boots, the first code it executes is called the BIOS, which is encoded into the computer's ROM. The BIOS reads the MBR into memory and transfers control to the code in the MBR.
The MBRs written by Microsoft partitioning tools, such as the one integrated into Windows 2000 Setup and the Disk Management MMC snap-in, go through a similar process of reading and transferring control. First, an MBR's code scans the primary partition table until it locates a partition containing a flag that signals the partition is bootable. When the MBR finds at least one such flag, it reads the first sector from the flagged partition into memory and transfers control to code within the partition. This type of partition is called a boot partition, and the first sector of such a partition is called a boot sector.
Operating systems generally write boot sectors to disk without a user's involvement. For example, when Windows 2000 Setup writes the MBR to a hard disk, it also writes a boot sector to the first bootable partition of the disk. You might have used the MS-DOS sys command to manually write MS-DOS boot sectors to disks. Windows 2000 Setup checks to see whether the boot sector it will overwrite with a Windows 2000 boot sector is a valid MS-DOS boot sector. If it is, Windows 2000 Setup copies the boot sector's contents to a file named Bootsect.dos in the root directory of the partition.
Before writing to a partition's boot sector, Windows 2000 Setup ensures that the partition is formatted with a file system that Windows 2000 supports (FAT, FAT32, or NTFS) by formatting the boot partition (and any other partition) with a file system type you specify. If partitions are already formatted, you can instruct Setup to skip this step. After Setup formats the boot partition, Setup copies the files Windows 2000 uses to the logical disk drive, including two files that are part of the boot sequence, Ntldr and Ntdetect.com.
Another of Setup's roles is to create a boot menu file, Boot.ini, in the root directory of the boot partition. This file contains options for starting the version of Windows 2000 that Setup installs and any preexisting Windows 2000 installations. If Bootsect.dos contains a valid MS-DOS boot sector, one of the entries Boot.ini creates is to boot into MS-DOS. The following output shows an example Boot.ini file from a dual-boot computer on which MS-DOS is installed before Windows 2000:
[boot loader] timeout=30 default=multi(0)disk(0)rdisk(0)partition(1)\WINNT [operating systems] multi(0)disk(0)rdisk(0)partition(1) \WINNT="Microsoft Windows 2000 Professional" /fastdetect C:\="Microsoft Windows"
Setup must know the partition format before it writes a boot sector because the contents of the boot sector vary depending on the format. For example, if the boot partition is a FAT partition, Windows 2000 writes code to the boot sector that understands the FAT file system. But if the partition is in NTFS format, Windows 2000 writes NTFS-capable code. The role of the boot-sector code is to give Windows 2000 information about the structure and format of a logical disk drive and to read in the Ntldr file from the root directory of the logical disk drive. Thus, the boot-sector code contains just enough read-only file system code to accomplish this task. After the boot-sector code loads Ntldr into memory, it transfers control to Ntldr's entry point. If the boot-sector code can't find Ntldr in the logical disk drive's root directory, it displays the error message "BOOT: Couldn't find NTLDR" if the boot file system is FAT or "NTLDR is missing" if the file system is NTFS.
Ntldr begins its existence while a system is executing in an x86 operating mode called real mode. In real mode, no virtual-to-physical translation of memory addresses occurs, which means that programs that use the memory addresses interpret them as physical addresses and that only the first 1 MB of the computer's physical memory is accessible. Simple MS-DOS programs execute in a real mode environment. However, the first action Ntldr takes is to switch the system to protected mode. Still no virtual-to-physical translation occurs at this point in the boot process, but a full 32 bits of memory becomes accessible. After the system is in protected mode, Ntldr can access all of physical memory. After creating enough page tables to make memory below 16 MB accessible with paging turned on, Ntldr enables paging. Protected mode with paging enabled is the mode in which Windows 2000 executes in normal operation.
After Ntldr enables paging, it is fully operational. However, it still relies on functions supplied by the boot code to access IDE-based system and boot disks as well as the display. The boot-code functions briefly switch off paging and switch the processor back to a mode in which services provided by the BIOS can be executed. If either the boot or system drives are SCSI-based, Ntldr loads a file named Ntbootdd.sys and uses it instead of the boot-code functions for disk access. Ntldr next reads the Boot.ini file from the root directory using built-in file system code. Like the boot sector's code, Ntldr contains read-only NTFS and FAT code; unlike the boot sector's code, however, Ntldr's file system code can read subdirectories.
Ntldr clears the screen and if there is more than one boot-selection entry in Boot.ini, it presents the user with the boot-selection menu. (If there is only one entry, Ntldr bypasses the menu and proceeds to displaying the startup progress bar.) Selection entries in Boot.ini direct Ntldr to the partition on which the Windows 2000 system directory (typically \Winnt) of the selected installation resides. This partition might be the same as the boot partition, or it might be another primary partition.
If the Boot.ini entry refers to an MS-DOS installation (that is, by referring to C:\ as the system partition), Ntldr reads the contents of the Bootsect.dos file into memory, switches back to 16-bit real mode, and calls the MBR code in Bootsect.dos. This action causes the Bootsect.dos code to execute as if the MBR had read the code from disk. Code in Bootsect.dos continues an MSDOSspecific boot, such as is used to boot Microsoft Windows 98 or Microsoft Windows 95 on a computer on which these operating systems are installed with Windows 2000.
Entries in Boot.ini can include optional arguments that Ntldr and other components involved in the boot process interpret. Table 4-2 contains a complete list of these options and their effects.
Table 4-2 Boot.ini Switches
|/3GB||Increases the size of the user process address space from 2 GB to 3 GB (and therefore reduces the size of system space from 2 GB to 1 GB). Giving virtual-memory-intensive applications such as database servers a larger address space can improve their performance. For an application to take advantage of this feature, however, two additional conditions must be met: the system must be running Windows 2000 Advanced Server or Datacenter Server and the application .exe must be flagged as a 3-GB-aware application. (See the section "Address Space Layout" in Chapter 7 for more information.)|
|/BASEVIDEO||Causes Windows 2000 to use the standard VGA display driver for GUI-mode operations.|
|/BAUDRATE=||Enables kernel-mode debugging and specifies anoverride for the default baud rate (19200) at which aremote kernel debugger host will connect. Example:/BAUDRATE=115200.|
|/BOOTLOG||Causes Windows 2000 to write a log of the boot to the file %SystemRoot%\Ntbtlog.txt.|
|/BREAK||Causes the hardware abstraction layer (HAL) to stop at a breakpoint at HAL initialization. The first thing the Windows 2000 kernel does when it initializes is to initialize the HAL, so this breakpoint is the earliest one possible. The HAL will wait indefinitely at the breakpoint until a kernel-debugger connection is made. If the switch is used without the /DEBUG switch, the system will Blue Screen with STOP code of 0x00000078 (PHASE0_EXCEPTION).|
|/BURNMEMORY=||Specifies an amount of memory Windows 2000 can't use (similar to the /MAXMEM switch). The value is specified in megabytes. Example: /BURNMEMORY=128 would indicate that Windows 2000 can't use 128 MB of the total physical memory on the machine.|
|/CLKLVL||Causes the standard x86 multiprocessor HAL (Halmps.dll) to configure itself for a level-sensitivesystem clock rather then an edge-triggered clock. Level-sensitive and edge-triggered are terms used to describe hardware interrupt types.|
|/CRASHDEBUG||Causes the kernel debugger to be loaded when the system boots, but to remain inactive unless a crash occurs. This allows the serial port that the kernel debugger would use to be available for use by the system until the system crashes (vs. /DEBUG, which causes the kernel debugger to use the serial port for the life of the system session).|
|/DEBUG||Enables kernel-mode debugging.|
|/DEBUGPORT=||Enables kernel-mode debugging and specifies an override for the default serial port (COM1) to which a remote kernel-debugger host is connected. Example:/DEBUGPORT=COM2.|
|/FASTDETECT||Default boot option for Windows 2000. Replaces the Windows NT 4 switch /NOSERIALMICE. The reason the qualifier exists (vs. just having NTDETECT perform this operation by default) is so that NTDETECT can support booting Windows NT 4. Windows 2000 Plug and Play device drivers perform detection of parallel and serial devices, but Windows NT 4 expects NTDETECT to perform the detection. Thus, specifying /FASTDETECT causes NTDETECT to skip parallel and serial deviceenumeration (actions that are not required when booting Windows 2000), whereas omitting the switch causes NTDETECT to perform this enumeration (which isrequired for booting Windows NT 4).|
|/INTAFFINITY||Directs the standard x86 multiprocessor HAL(Halmps.dll) to set interrupt affinities such that onlythe highest numbered processor will receive interrupts. Without the switch, the HAL defaults to its normalbehavior of letting all processors receive interrupts.|
|/KERNEL= /HAL=|| Enable you to override Ntldr's default filename for the kernel image (Ntoskrnl.exe) and/or the HAL (Hal.dll). These options are useful for alternating between a checked kernel environment and a free (retail) kernel environment or even to manually select a different HAL. If you want to boot a checked environment that consists solely of the checked kernel and HAL, which is typically all that is needed to test drivers, follow these steps on a system installed with the free build: |
|/MAXMEM=||Limits Windows 2000 to ignore (not use) physicalmemory beyond the amount indicated. The number is interpreted in megabytes. Example: /MAXMEM=32 would limit the system to using the first 32 MB ofphysical memory even if more were present.|
|/MAXPROCSPERCLUSTER=||For the standard x86 multiprocessor HAL (Halmps.dll), forces cluster-mode Advanced Programmable Interrupt Controller (APIC) addressing (not supported on systems with an 82489DX external APIC interrupt controller).|
|/NODEBUG||Prevents kernel-mode debugging from being initialized. Overrides the specification of any of the three debug-related switches, /DEBUG, /DEBUGPORT, and/BAUDRATE.|
|/NOGUIBOOT||Instructs Windows 2000 not to initialize the VGA video driver responsible for presenting bitmapped graphics during the boot process. The driver is used to display boot progress information, so disabling it will disable the ability of Windows 2000 to show this information.|
|/NOLOWMEM||Requires that the /PAE switch be present and that the system have more than 4 GB of physical memory. If these conditions are met, the PAE-enabled version of theWindows 2000 kernel, Ntkrnlpa.exe, won't use the first 4 GB of physical memory. Instead, it will load all applications and device drivers, and allocate all memory pools, from above that boundary. This switch is useful only to test device driver compatibility with large memory systems.|
|/NOPAE||Forces Ntldr to load the non-Physical Address Extension (PAE) version of the Windows 2000 kernel, even if the system is detected as supporting x86 PAEs and has more than 4 GB of physical memory.|
|/NOSERIALMICE=[COMx |COMx,y,z...]||Obsolete Windows NT 4 qualifier—replaced by the absence of the /FASTDETECT switch. Disables serial mouse detection of the specified COM ports. Thisswitch was used if you had a device other than a mouse attached to a serial port during the startup sequence. Using /NOSERIALMICE without specifying a COM port disables serial mouse detection on all COM ports. See Microsoft Knowledge Base article Q131976 for more information.|
|/NUMPROC=||Specifies the number of CPUs that can be used on a multiprocessor system. Example: /NUMPROC=2 on a four-way system will prevent Windows 2000 from using two of the four processors.|
|/ONECPU||Causes Windows 2000 to use only one CPU on a multiprocessor system.|
|/PAE||Causes Ntldr to load Ntkrnlpa.exe, which is the version of the x86 kernel that is able to take advantage of x86 PAEs. The PAE version of the kernel presents 64-bit physical addresses to device drivers, so this switch is helpful for testing device driver support for large memory systems|
|/PCILOCK||Stops Windows 2000 from dynamically assigning IO/IRQ resources to PCI devices and leaves the devices configured by the BIOS. See Microsoft Knowledge Base article Q148501 for more information|
|/SAFEBOOT:||Specifies options for a safe boot. You should never have to specify this option manually, since Ntldr specifies it for you when you use the F8 menu to perform a safe boot. (A safe boot is a boot in which Windows 2000 only loads drivers and services that are specified by name or group under the Minimal or Network registry keys underHKLM\SYSTEM\CurrentControlSet\Control\SafeBoot.) Following the colon in the option you must specify one of three additional switches: MINIMAL, NETWORK, or DSREPAIR. The MINIMAL and NETWORK flags correspond to safe boot with no network and safe boot with network support, respectively. The DSREPAIR (Directory Services Repair) switch causes Windows 2000 to boot into a mode in which it restores the Active Directory directory service from a backup medium you present. An additional option you can append is (ALTERNATESHELL), which tells Windows 2000 to use the program specified by the HKLM\SYSTEM\CurrentControlSet\SafeBoot\AlternateShell value as the graphical shell rather than to use the default, which is Windows Explorer.|
|/SCSIORDINAL:||Directs Windows 2000 to the SCSI ID of the controller. (Adding a new SCSI device to a system with an on-board SCSI controller can cause the controller's SCSI ID to change.) See Microsoft Knowledge Base article Q103625 for more information.|
|/SOS||Causes Windows 2000 to list the device drivers marked to load at boot time and then to display the system version number (including the build number), amount of physical memory, and number of processors.|
|/TIMERES=|| Sets the resolution of the system timer on the standard x86 multiprocessor HAL (Halmps.dll). The argument is a number interpreted in hundreds of nanoseconds, but the rate is set to the closest resolution the HAL supports that isn't larger than the one requested. The HAL supports the following resolutions: |
The default resolution is 7.8 ms. The system timer resolution affects the resolution of waitable timers. Example: /TIMERES=21000 would set the timer to a resolution of 2.0 ms.
|/USE8254||Instructs the HAL to use the 8254 timer chip as its base timer (for systems with older BIOS's). See MicrosoftKnowledge Base article Q169901 for more information.|
|/WIN95||Directs Ntldr to boot the Consumer Windows boot sector stored in Bootsect.w40. This switch is pertinent only on a triple-boot system that has MS-DOS, Consumer Windows, and Windows 2000 installed. See Microsoft Knowledge Base article Q157992 for more information.|
|/WIN95DOS||Directs Ntldr to boot the MS-DOS boot sector stored in Bootsect.dos. This switch is pertinent only on a triple-boot system that has MS-DOS, Consumer Windows, and Windows 2000 installed. See Microsoft Knowledge Base article Q157992 for more information.|
|/YEAR=||Instructs the Windows 2000 core time function to ignore the year that the computer's real-time clock reports and instead use the one indicated. Thus, the year used in the switch affects every piece of software on the system,including the Windows 2000 kernel. Example:/YEAR=2001. (This switch was created to assist in Y2K testing.)|
If the user doesn't select an entry from the selection menu within the timeout period the Boot.ini file specifies, Ntldr chooses the default selection. Once the boot selection has been made, Ntldr loads and executes Ntdetect.com, a 16-bit real-mode program that uses a system's BIOS to query the computer for basic device and configuration information. This information includes the following:
This information is gathered into internal data structures that will be stored under the HKLM\HARDWARE\DESCRIPTION registry key later in the boot.
Ntldr then clears the screen and displays the "Starting Windows" progress bar. This progress bar remains empty until Ntldr begins loading boot drivers. (See step 5 in the following list.) Below the progress bar is the message "For troubleshooting and advanced startup options for Windows 2000, press F8." If the user presses F8, the advanced boot menu is presented, which allows the user to select such options as booting from last known good, safe mode, debug mode, and so on.
Next, Ntldr begins loading the files from the boot partition needed to start the kernel initialization:
Figure 4-2 Logical Disk Manager driver service settings
This action is the end of Ntldr's role in the boot process. At this point, Ntldr calls the main function in Ntoskrnl.exe to perform the rest of the system initialization.
When Ntldr calls Ntoskrnl, it passes a data structure that contains a copy of the line in Boot.ini that represents the selected menu option for this boot, a pointer to the memory tables Ntldr generated to describe the physical memory on the system, a pointer to the in-memory copy of the HARDWARE and SYSTEM registry hives, and a pointer to the list of boot drivers Ntldr loaded.
Ntoskrnl then begins the first of its two-phase initialization process, called phase 0 and phase 1. Most executive subsystems have an initialization function that takes a parameter that identifies which phase is executing.
During phase 0, interrupts are disabled. The purpose of this phase is to build the rudimentary structures required to allow the services needed in phase 1 to be invoked. Ntoskrnl's main function calls KiSystemStartup, which in turn calls HalInitializeProcessor and KiInitializeKernel for each CPU. KiInitializeKernel, if running on the boot CPU, performs systemwide kernel initialization, such as initializing internal listheads and other data structures that all CPUs share. Each instance of KiInitializeKernel then calls the function responsible for orchestrating phase 0, ExpInitializeExecutive.
ExpInitializeExecutive starts by calling the HAL function HalInitSystem, which gives the HAL a chance to gain system control before Windows 2000 performs significant further initialization. One responsibility of HalInitSystem is to prepare the system interrupt controller of each CPU for interrupts and to configure the interval clock timer interrupt, which is used for CPU time accounting. (See the section "Quantum Accounting"in Chapter 6 for more on CPU time accounting.)
Only on the boot processor does ExpInitializeExecutive perform initialization other than calling HalInitSystem. When HalInitSystem returns control, ExpInitializeExecutive on the boot CPU proceeds by processing the/BURNMEMORY Boot.ini switch (if the switch is present in the line from the Boot.ini file that corresponds to the menu selection the user made when choosing which installation to boot) and discarding the amount of memory the switch specifies.
Next, ExpInitializeExecutive calls the phase 0 initialization routines for the memory manager, object manager, security reference monitor, process manager, and Plug and Play manager. These components perform the following initialization steps:
When control returns to the KiInitializeKernel function on each processor, control proceeds to the Idle loop, which then causes the system thread created in step 4 of the previous process description to begin executing phase 1. (Secondary processors wait to begin their initialization until step 5 of phase 1, described in the following list.) Phase 1 consists of the following steps. (The steps at which the progress bar on the screen is updated are included in this list.)
The I/O manager first initializes various internal structures and creates the driver and device object types. It then calls the Plug and Play manager, power manager, and the HAL to begin the various stages of dynamic device enumeration and initialization. (Because this process is complex and specific to the I/O system, we'll save the details for Chapter 9.) Then the Windows Management Instrumentation (WMI) subsystem is initialized, which provides WMI support for device drivers that adhere to the Windows Driver Model(WDM). (See the section "Windows Management Instrumentation" in Chapter 5 for more information.) Next, all the boot-start drivers are called to perform their driver-specific initialization, and the system-start device drivers are loaded and initialized. (Details on the processing of the driver load control information on the registry are also covered in Chapter 9.) Finally, the MS-DOS device names are created as symbolic links in the object manager's namespace.
As a final step before considering the executive and kernel initialization complete, the phase 1 initialization thread waits on the handle to the Session Manager process with a timeout value of 5 seconds. If the Session Manager process exits before the 5 seconds elapses, the system crashes itself with a SESSION5_INITIALIZATION_FAILED bug check code.
If the 5-second wait times out (that is, if 5 seconds elapse), the Session Manager is assumed to have started successfully, and the phase 1 initialization function calls the memory manager's zero page thread function (explained in Chapter 7). Thus, this system thread becomes the zero page thread for the remainder of the life of the system.
Smss is like any other user-mode process except for two differences: First, Windows 2000 considers Smss a trusted part of the operating system. Second, Smss is a native application. Because it's a trusted operating system component, Smss can perform actions few other processes can perform, such as creating security tokens. Because it's a native application, Smss doesn't use Win32 APIs—it uses only core executive APIs known collectively as the Windows 2000 native API. Smss doesn't use the Win32 APIs because the Win32 subsystem isn't executing when Smss launches. In fact, one of Smss's first tasks is to start the Win32 subsystem.
Smss then calls the configuration manager executive subsystem to finish initializing the registry, fleshing the registry out to include all its keys. The configuration manager is programmed to know where the core registry hives are stored on disk (excluding hives corresponding to user profiles) and records the paths to the hives it loads in the HKLM\SYSTEM\CurrentControlSet\Control\hivelist key.
The main thread of Smss performs the following initialization steps:
After performing these initialization steps, the main thread in Smss waits forever on the process handles to Csrss and Winlogon. If either of these processes terminates unexpectedly, Smss crashes the system, since Windows 2000 relies on their existence.
Winlogon then performs its startup steps, such as creating the initial window station and desktop objects, loading GINA DLLs, and so on. It then creates the service control manager (SCM) process (\Winnt\System32\Services.exe), which loads all services and device drivers marked for auto-start, and the local security authentication subsystem (Lsass) process (\Winnt\System32\Lsass.exe). (For more details on the startup sequence for Winlogon and Lsass, see the section "Winlogon Initialization"in Chapter 8.)
After the SCM initializes the auto-start services and drivers and a user has successfully logged on at the console, the SCM deems the boot successful. The registry last known good control set (as indicated by HKLM\SYSTEM\Select\LastKnownGood) is updated to match \CurrentControlSet. If a user chooses to boot to the last known good menu during the first steps of a boot, or if a driver returns a severe or critical error, the system uses the LastKnownGood value as the current control set. Doing so increases the chances that the system will boot successfully, because at least one previous boot using the last known good profile was successful.
That action brings us to the end of the boot process.