/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    ioinit.c

Abstract:

    This module contains the code to initialize the I/O system.

--*/

#include "iomgr.h"
#include <setupblk.h>
#include <inbv.h>
#include <ntddstor.h>
#include <hdlsblk.h>
#include <hdlsterm.h>


//
// Define the default number of IRP that can be in progress and allocated
// from a lookaside list.
//

#define DEFAULT_LOOKASIDE_IRP_LIMIT 512

//
// I/O Error logging support
//
PVOID IopErrorLogObject = NULL;

//
// Define a macro for initializing drivers.
//

#define InitializeDriverObject( Object ) {                                 \
    ULONG i;                                                               \
    RtlZeroMemory( Object,                                                 \
                   sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION )); \
    Object->DriverExtension = (PDRIVER_EXTENSION) (Object + 1);            \
    Object->DriverExtension->DriverObject = Object;                        \
    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)                         \
        Object->MajorFunction[i] = IopInvalidDeviceRequest;                \
    Object->Type = IO_TYPE_DRIVER;                                         \
    Object->Size = sizeof( DRIVER_OBJECT );                                \
    }

ULONG   IopInitFailCode;    // Debugging aid for IoInitSystem

//
// Define external procedures not in common header files
//

VOID
IopInitializeData(
    VOID
    );

//
// Define the local procedures
//

BOOLEAN
IopCreateObjectTypes(
    VOID
    );

BOOLEAN
IopCreateRootDirectories(
    VOID
    );

NTSTATUS
IopInitializeAttributesAndCreateObject(
    IN PUNICODE_STRING ObjectName,
    IN OUT POBJECT_ATTRIBUTES ObjectAttributes,
    OUT PDRIVER_OBJECT *DriverObject
    );

BOOLEAN
IopReassignSystemRoot(
    IN PLOADER_PARAMETER_BLOCK LoaderBlock,
    OUT PSTRING NtDeviceName
    );

VOID
IopSetIoRoutines(
    IN VOID
    );

VOID
IopStoreSystemPartitionInformation(
    IN     PUNICODE_STRING NtSystemPartitionDeviceName,
    IN OUT PUNICODE_STRING OsLoaderPathName
    );

NTSTATUS
IopCreateArcNamesDisk(
    IN  PLOADER_PARAMETER_BLOCK LoaderBlock,
    IN  BOOLEAN                 SingleBiosDiskFound,
    OUT BOOLEAN                 *BootDiskFound
    );

NTSTATUS
IopCreateArcNamesCd(
    IN  PLOADER_PARAMETER_BLOCK LoaderBlock
    );

//
// The following allows the I/O system's initialization routines to be
// paged out of memory.
//

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,IoInitSystem)
#pragma alloc_text(INIT,IopCreateArcNames)
#pragma alloc_text(INIT,IopCreateArcNamesDisk)
#pragma alloc_text(INIT,IopCreateArcNamesCd)
#pragma alloc_text(INIT,IopCreateObjectTypes)
#pragma alloc_text(INIT,IopCreateRootDirectories)
#pragma alloc_text(INIT,IopInitializeAttributesAndCreateObject)
#pragma alloc_text(INIT,IopInitializeBuiltinDriver)
#pragma alloc_text(INIT,IopMarkBootPartition)
#pragma alloc_text(INIT,IopReassignSystemRoot)
#pragma alloc_text(INIT,IopSetIoRoutines)
#pragma alloc_text(INIT,IopStoreSystemPartitionInformation)
#pragma alloc_text(INIT,IopInitializeReserveIrp)
#endif

#define DEVICENAME_BUFFER_LENGTH        256
#define VALUE_BUFFER_LENGTH             32

BOOLEAN
IoInitSystem(
    PLOADER_PARAMETER_BLOCK LoaderBlock
    )

/*++

Routine Description:

    This routine initializes the I/O system.

Arguments:

    LoaderBlock - Supplies a pointer to the loader parameter block that was
        created by the OS Loader.

Return Value:

    The function value is a BOOLEAN indicating whether or not the I/O system
    was successfully initialized.

--*/

{
    PDRIVER_OBJECT driverObject;
    PDRIVER_OBJECT *nextDriverObject;
    STRING ntDeviceName;
    ULONG largePacketSize;
    ULONG smallPacketSize;
    ULONG mdlPacketSize;
    LARGE_INTEGER deltaTime;
    MM_SYSTEMSIZE systemSize;
    USHORT completionZoneSize;
    USHORT largeIrpZoneSize;
    USHORT smallIrpZoneSize;
    USHORT mdlZoneSize;
    ULONG oldNtGlobalFlag;
    NTSTATUS status;
    union {
        ANSI_STRING ansiString;
        UNICODE_STRING eventName;
        UNICODE_STRING startTypeName;
    } LocalStrings;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE handle;
    PGENERAL_LOOKASIDE lookaside;
    ULONG lookasideIrpLimit;
    ULONG lookasideSize;
    ULONG Index;
    PKPRCB prcb;
    PKEY_VALUE_PARTIAL_INFORMATION value;
    PUCHAR valueBuffer;

    ASSERT( IopQueryOperationLength[FileMaximumInformation] == 0xff );
    ASSERT( IopSetOperationLength[FileMaximumInformation] == 0xff );
    ASSERT( IopQueryOperationAccess[FileMaximumInformation] == 0xffffffff );
    ASSERT( IopSetOperationAccess[FileMaximumInformation] == 0xffffffff );

    ASSERT( IopQueryFsOperationLength[FileFsMaximumInformation] == 0xff );
    ASSERT( IopSetFsOperationLength[FileFsMaximumInformation] == 0xff );
    ASSERT( IopQueryFsOperationAccess[FileFsMaximumInformation] == 0xffffffff );
    ASSERT( IopSetFsOperationAccess[FileFsMaximumInformation] == 0xffffffff );

    //
    // Initialize the I/O database resource, lock, and the file system and
    // network file system queue headers.  Also allocate the cancel spin
    // lock.
    //

    ntDeviceName.Buffer = ExAllocatePoolWithTag( NonPagedPool,
                                                 DEVICENAME_BUFFER_LENGTH +
                                                 VALUE_BUFFER_LENGTH,
                                                 'oI' );

    if (ntDeviceName.Buffer == NULL) {

        IopInitFailCode = 0;
        return FALSE;
    }
    
    ntDeviceName.MaximumLength = DEVICENAME_BUFFER_LENGTH;
    ntDeviceName.Length = 0;

    valueBuffer = (PUCHAR) ntDeviceName.Buffer + DEVICENAME_BUFFER_LENGTH;

    ExInitializeResourceLite( &IopDriverLoadResource );
    ExInitializeResourceLite( &IopDatabaseResource );
    ExInitializeResourceLite( &IopSecurityResource );
    ExInitializeResourceLite( &IopCrashDumpLock );
    InitializeListHead( &IopDiskFileSystemQueueHead );
    InitializeListHead( &IopCdRomFileSystemQueueHead );
    InitializeListHead( &IopTapeFileSystemQueueHead );
    InitializeListHead( &IopNetworkFileSystemQueueHead );
    InitializeListHead( &IopBootDriverReinitializeQueueHead );
    InitializeListHead( &IopDriverReinitializeQueueHead );
    InitializeListHead( &IopNotifyShutdownQueueHead );
    InitializeListHead( &IopNotifyLastChanceShutdownQueueHead );
    InitializeListHead( &IopFsNotifyChangeQueueHead );
    KeInitializeSpinLock( &IoStatisticsLock );

    IopSetIoRoutines();
    //
    // Initialize the unique device object number counter used by IoCreateDevice
    // when automatically generating a device object name.
    //
    IopUniqueDeviceObjectNumber = 0;

    //
    // Initialize the large I/O Request Packet (IRP) lookaside list head and the
    // mutex which guards the list.
    //


    if (!IopLargeIrpStackLocations) {
        IopLargeIrpStackLocations = DEFAULT_LARGE_IRP_LOCATIONS;
        IopIrpStackProfiler.Flags |= IOP_ENABLE_AUTO_SIZING;
    }

    systemSize = MmQuerySystemSize();

    switch ( systemSize ) {

    case MmSmallSystem :
        completionZoneSize = 6;
        smallIrpZoneSize = 6;
        largeIrpZoneSize = 8;
        mdlZoneSize = 16;
        lookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT;
        break;

    case MmMediumSystem :
        completionZoneSize = 24;
        smallIrpZoneSize = 24;
        largeIrpZoneSize = 32;
        mdlZoneSize = 90;
        lookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT * 2;
        break;

    case MmLargeSystem :
    default :
        if (MmIsThisAnNtAsSystem()) {
            completionZoneSize = 96;
            smallIrpZoneSize = 96;
            largeIrpZoneSize = 128;
            mdlZoneSize = 256;
            lookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT * 128; // 64k

        } else {
            completionZoneSize = 32;
            smallIrpZoneSize = 32;
            largeIrpZoneSize = 64;
            mdlZoneSize = 128;
            lookasideIrpLimit = DEFAULT_LOOKASIDE_IRP_LIMIT * 3;
        }

        break;
    }

    //
    // Initialize the system I/O completion lookaside list.
    //

    ExInitializeSystemLookasideList( &IopCompletionLookasideList,
                                     NonPagedPool,
                                     sizeof(IOP_MINI_COMPLETION_PACKET),
                                     ' pcI',
                                     completionZoneSize,
                                     &ExSystemLookasideListHead );


    //
    // Initialize the system large IRP lookaside list.
    //

    largePacketSize = (ULONG) (sizeof( IRP ) + (IopLargeIrpStackLocations * sizeof( IO_STACK_LOCATION )));
    ExInitializeSystemLookasideList( &IopLargeIrpLookasideList,
                                     NonPagedPool,
                                     largePacketSize,
                                     'lprI',
                                     largeIrpZoneSize,
                                     &ExSystemLookasideListHead );

    //
    // Initialize the system small IRP lookaside list.
    //


    smallPacketSize = (ULONG) (sizeof( IRP ) + sizeof( IO_STACK_LOCATION ));
    ExInitializeSystemLookasideList( &IopSmallIrpLookasideList,
                                     NonPagedPool,
                                     smallPacketSize,
                                     'sprI',
                                     smallIrpZoneSize,
                                     &ExSystemLookasideListHead );

    //
    // Initialize the system MDL lookaside list.
    //

    mdlPacketSize = (ULONG) (sizeof( MDL ) + (IOP_FIXED_SIZE_MDL_PFNS * sizeof( PFN_NUMBER )));
    ExInitializeSystemLookasideList( &IopMdlLookasideList,
                                     NonPagedPool,
                                     mdlPacketSize,
                                     ' ldM',
                                     mdlZoneSize,
                                     &ExSystemLookasideListHead );

    //
    // Compute the lookaside IRP float credits per processor.
    //

    lookasideIrpLimit /= KeNumberProcessors;

    //
    // Initialize the per processor nonpaged lookaside lists and descriptors.
    //
    // N.B. All the I/O related lookaside list structures are allocated at
    //      one time to make sure they are aligned, if possible, and to avoid
    //      pool overhead.
    //

    lookasideSize = 4 * KeNumberProcessors * sizeof(GENERAL_LOOKASIDE);
    lookaside = ExAllocatePoolWithTag( NonPagedPool, lookasideSize, 'oI');
    for (Index = 0; Index < (ULONG)KeNumberProcessors; Index += 1) {
        prcb = KiProcessorBlock[Index];

        //
        // Set the per processor IRP float credits.
        //

        prcb->LookasideIrpFloat = lookasideIrpLimit;

        //
        // Initialize the I/O completion per processor lookaside pointers
        //

        prcb->PPLookasideList[LookasideCompletionList].L = &IopCompletionLookasideList;
        if (lookaside != NULL) {
            ExInitializeSystemLookasideList( lookaside,
                                             NonPagedPool,
                                             sizeof(IOP_MINI_COMPLETION_PACKET),
                                             'PpcI',
                                             completionZoneSize,
                                             &ExSystemLookasideListHead );

            prcb->PPLookasideList[LookasideCompletionList].P = lookaside;
            lookaside += 1;

        } else {
            prcb->PPLookasideList[LookasideCompletionList].P = &IopCompletionLookasideList;
        }

        //
        // Initialize the large IRP per processor lookaside pointers.
        //

        prcb->PPLookasideList[LookasideLargeIrpList].L = &IopLargeIrpLookasideList;
        if (lookaside != NULL) {
            ExInitializeSystemLookasideList( lookaside,
                                             NonPagedPool,
                                             largePacketSize,
                                             'LprI',
                                             largeIrpZoneSize,
                                             &ExSystemLookasideListHead );

            prcb->PPLookasideList[LookasideLargeIrpList].P = lookaside;
            lookaside += 1;

        } else {
            prcb->PPLookasideList[LookasideLargeIrpList].P = &IopLargeIrpLookasideList;
        }

        //
        // Initialize the small IRP per processor lookaside pointers.
        //

        prcb->PPLookasideList[LookasideSmallIrpList].L = &IopSmallIrpLookasideList;
        if (lookaside != NULL) {
            ExInitializeSystemLookasideList( lookaside,
                                             NonPagedPool,
                                             smallPacketSize,
                                             'SprI',
                                             smallIrpZoneSize,
                                             &ExSystemLookasideListHead );

            prcb->PPLookasideList[LookasideSmallIrpList].P = lookaside;
            lookaside += 1;

        } else {
            prcb->PPLookasideList[LookasideSmallIrpList].P = &IopSmallIrpLookasideList;
        }

        //
        // Initialize the MDL per processor lookaside list pointers.
        //

        prcb->PPLookasideList[LookasideMdlList].L = &IopMdlLookasideList;
        if (lookaside != NULL) {
            ExInitializeSystemLookasideList( lookaside,
                                             NonPagedPool,
                                             mdlPacketSize,
                                             'PldM',
                                             mdlZoneSize,
                                             &ExSystemLookasideListHead );

            prcb->PPLookasideList[LookasideMdlList].P = lookaside;
            lookaside += 1;

        } else {
            prcb->PPLookasideList[LookasideMdlList].P = &IopMdlLookasideList;
        }
    }

    //
    // Initialize the error log spin locks and log list.
    //

    KeInitializeSpinLock( &IopErrorLogLock );
    InitializeListHead( &IopErrorLogListHead );

    if (IopInitializeReserveIrp(&IopReserveIrpAllocator) == FALSE) {
        IopInitFailCode = 1;
        return FALSE;
    }

    if (IopIrpAutoSizingEnabled() && !NT_SUCCESS(IopInitializeIrpStackProfiler())) {
        IopInitFailCode = 13;
        return FALSE;
    }

    //
    // Determine if the Error Log service will ever run this boot.
    //
    InitializeObjectAttributes (&objectAttributes,
                                &CmRegistryMachineSystemCurrentControlSetServicesEventLog,
                                OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
                                (HANDLE) NULL,
                                (PSECURITY_DESCRIPTOR) NULL );

    status = ZwOpenKey(&handle,
                       KEY_READ,
                       &objectAttributes
                       );

    if (NT_SUCCESS (status)) {
        RtlInitUnicodeString (&LocalStrings.startTypeName, L"Start");
        value = (PKEY_VALUE_PARTIAL_INFORMATION) valueBuffer;
        status = NtQueryValueKey (handle,
                                  &LocalStrings.startTypeName,
                                  KeyValuePartialInformation,
                                  valueBuffer,
                                  VALUE_BUFFER_LENGTH,
                                  &Index);

        if (NT_SUCCESS (status) && (value->Type == REG_DWORD)) {
            if (SERVICE_DISABLED == (*(PULONG) (value->Data))) {
                //
                // We are disabled for this boot.
                //
                IopErrorLogDisabledThisBoot = TRUE;
            } else {
                IopErrorLogDisabledThisBoot = FALSE;
            }
        } else {
            //
            // Didn't find the value so we are not enabled.
            //
            IopErrorLogDisabledThisBoot = TRUE;
        }
        ObCloseHandle(handle, KernelMode);
    } else {
        //
        // Didn't find the key so we are not enabled
        //
        IopErrorLogDisabledThisBoot = TRUE;
    }

    //
    // Initialize the timer database and start the timer DPC routine firing
    // so that drivers can use it during initialization.
    //

    deltaTime.QuadPart = - 10 * 1000 * 1000;

    KeInitializeSpinLock( &IopTimerLock );
    InitializeListHead( &IopTimerQueueHead );
    KeInitializeDpc( &IopTimerDpc, IopTimerDispatch, NULL );
    KeInitializeTimerEx( &IopTimer, SynchronizationTimer );
    (VOID) KeSetTimerEx( &IopTimer, deltaTime, 1000, &IopTimerDpc );

    //
    // Initialize the IopHardError structure used for informational pop-ups.
    //

    ExInitializeWorkItem( &IopHardError.ExWorkItem,
                          IopHardErrorThread,
                          NULL );

    InitializeListHead( &IopHardError.WorkQueue );

    KeInitializeSpinLock( &IopHardError.WorkQueueSpinLock );

    KeInitializeSemaphore( &IopHardError.WorkQueueSemaphore,
                           0,
                           MAXLONG );

    IopHardError.ThreadStarted = FALSE;

    IopCurrentHardError = NULL;

    //
    // Create the link tracking named event.
    //

    RtlInitUnicodeString( &LocalStrings.eventName, L"\\Security\\TRKWKS_EVENT" );
    InitializeObjectAttributes( &objectAttributes,
                                &LocalStrings.eventName,
                                OBJ_PERMANENT|OBJ_KERNEL_HANDLE,
                                (HANDLE) NULL,
                                (PSECURITY_DESCRIPTOR) NULL );
    status = NtCreateEvent( &handle,
                            EVENT_ALL_ACCESS,
                            &objectAttributes,
                            NotificationEvent,
                            FALSE );

    if (!NT_SUCCESS( status )) {

#if DBG
        DbgPrint( "IOINIT: NtCreateEvent failed\n" );
#endif
        HeadlessKernelAddLogEntry(HEADLESS_LOG_EVENT_CREATE_FAILED, NULL);
        return FALSE;
    }

    (VOID) ObReferenceObjectByHandle( handle,
                                      0,
                                      ExEventObjectType,
                                      KernelMode,
                                      (PVOID *) &IopLinkTrackingServiceEvent,
                                      NULL );

    KeInitializeEvent( &IopLinkTrackingPacket.Event, NotificationEvent, FALSE );
    KeInitializeEvent(&IopLinkTrackingPortObject, SynchronizationEvent, TRUE );
    ObCloseHandle(handle, KernelMode);

    //
    // Create all of the objects for the I/O system.
    //

    if (!IopCreateObjectTypes()) {

#if DBG
        DbgPrint( "IOINIT: IopCreateObjectTypes failed\n" );
#endif

        HeadlessKernelAddLogEntry(HEADLESS_LOG_OBJECT_TYPE_CREATE_FAILED, NULL);
        IopInitFailCode = 2;
        return FALSE;
    }

    //
    // Create the root directories for the I/O system.
    //

    if (!IopCreateRootDirectories()) {

#if DBG
        DbgPrint( "IOINIT: IopCreateRootDirectories failed\n" );
#endif

        HeadlessKernelAddLogEntry(HEADLESS_LOG_ROOT_DIR_CREATE_FAILED, NULL);
        IopInitFailCode = 3;
        return FALSE;
    }

    //
    // Initialize PlugPlay services phase 0
    //

    status = IopInitializePlugPlayServices(LoaderBlock, 0);
    if (!NT_SUCCESS(status)) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_PNP_PHASE0_INIT_FAILED, NULL);
        IopInitFailCode = 4;
        return FALSE;
    }

    //
    // Call Power manager to initialize for drivers
    //

    PoInitDriverServices(0);

    //
    // Call HAL to initialize PnP bus driver
    //

    HalInitPnpDriver();

    IopMarkHalDeviceNode();

    //
    // Call WMI to initialize it and allow it to create its driver object
    // Note that no calls to WMI can occur until it is initialized here.
    //

    WMIInitialize(0, (PVOID)LoaderBlock);

    //
    // Save this for use during PnP enumeration -- we NULL it out later
    // before LoaderBlock is reused.
    //

    IopLoaderBlock = (PVOID)LoaderBlock;

    //
    // If this is a remote boot, we need to add a few values to the registry.
    //

    if (IoRemoteBootClient) {
        status = IopAddRemoteBootValuesToRegistry(LoaderBlock);
        if (!NT_SUCCESS(status)) {
            KeBugCheckEx( NETWORK_BOOT_INITIALIZATION_FAILED,
                          1,
                          status,
                          0,
                          0 );
        }
    }

    //
    // Initialize PlugPlay services phase 1 to execute firmware mapper
    //

    status = IopInitializePlugPlayServices(LoaderBlock, 1);
    if (!NT_SUCCESS(status)) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_PNP_PHASE1_INIT_FAILED, NULL);
        IopInitFailCode = 5;
        return FALSE;
    }

    //
    // Initialize the drivers loaded by the boot loader (OSLOADER)
    //

    nextDriverObject = &driverObject;
    if (!IopInitializeBootDrivers( LoaderBlock,
                                   nextDriverObject )) {

#if DBG
        DbgPrint( "IOINIT: Initializing boot drivers failed\n" );
#endif // DBG

        HeadlessKernelAddLogEntry(HEADLESS_LOG_BOOT_DRIVERS_INIT_FAILED, NULL);
        IopInitFailCode = 6;
        return FALSE;
    }

    //
    // Once we have initialized the boot drivers, we don't need the
    // copy of the pointer to the loader block any more.
    //

    IopLoaderBlock = NULL;

    //
    // If this is a remote boot, start the network and assign
    // C: to \Device\LanmanRedirector.
    //

    if (IoRemoteBootClient) {
        status = IopStartNetworkForRemoteBoot(LoaderBlock);
        if (!NT_SUCCESS( status )) {
            KeBugCheckEx( NETWORK_BOOT_INITIALIZATION_FAILED,
                          2,
                          status,
                          0,
                          0 );
        }
    }

    //
    // Do last known good boot processing. If this is a last known good boot,
    // we will copy over the last known good drivers and files. Otherwise we
    // will ensure this boot doesn't taint our last good info (in case we crash
    // before the boot is marked good). Note that loading of the correct boot
    // drivers was handled by the boot loader, who chose an LKG boot in the
    // first place.
    //
    PpLastGoodDoBootProcessing();

    //
    // Save the current value of the NT Global Flags and enable kernel debugger
    // symbol loading while drivers are being loaded so that systems can be
    // debugged regardless of whether they are free or checked builds.
    //

    oldNtGlobalFlag = NtGlobalFlag;

    if (!(NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)) {
        NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
    }

    status = PsLocateSystemDll(FALSE);
    if (!NT_SUCCESS( status )) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_LOCATE_SYSTEM_DLL_FAILED, NULL);
        IopInitFailCode = 7;
        return FALSE;
    }

    //
    // Notify the boot prefetcher of boot progress.
    //

    CcPfBeginBootPhase(PfSystemDriverInitPhase);

    //
    // Initialize the device drivers for the system.
    //

    if (!IopInitializeSystemDrivers()) {
#if DBG
        DbgPrint( "IOINIT: Initializing system drivers failed\n" );
#endif // DBG

        HeadlessKernelAddLogEntry(HEADLESS_LOG_SYSTEM_DRIVERS_INIT_FAILED, NULL);
        IopInitFailCode = 8;
        return FALSE;
    }

    IopCallDriverReinitializationRoutines();

    //
    // Reassign \SystemRoot to NT device name path.
    //

    if (!IopReassignSystemRoot( LoaderBlock, &ntDeviceName )) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_ASSIGN_SYSTEM_ROOT_FAILED, NULL);
        IopInitFailCode = 9;
        return FALSE;
    }

    //
    // Protect the system partition of an ARC system if necessary
    //

    if (!IopProtectSystemPartition( LoaderBlock )) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_PROTECT_SYSTEM_ROOT_FAILED, NULL);
        IopInitFailCode = 10;
        return FALSE;
    }

    //
    // Assign DOS drive letters to disks and cdroms and define \SystemRoot.
    //

    LocalStrings.ansiString.MaximumLength = NtSystemRoot.MaximumLength / sizeof( WCHAR );
    LocalStrings.ansiString.Length = 0;
    LocalStrings.ansiString.Buffer = (RtlAllocateStringRoutine)( LocalStrings.ansiString.MaximumLength );
    status = RtlUnicodeStringToAnsiString( &LocalStrings.ansiString,
                                           &NtSystemRoot,
                                           FALSE
                                         );
    if (!NT_SUCCESS( status )) {

        DbgPrint( "IOINIT: UnicodeToAnsi( %wZ ) failed - %x\n", &NtSystemRoot, status );

        HeadlessKernelAddLogEntry(HEADLESS_LOG_UNICODE_TO_ANSI_FAILED, NULL);
        IopInitFailCode = 11;
        return FALSE;
    }

    IoAssignDriveLetters( LoaderBlock,
                          &ntDeviceName,
                          (PUCHAR) LocalStrings.ansiString.Buffer,
                          &LocalStrings.ansiString );

    status = RtlAnsiStringToUnicodeString( &NtSystemRoot,
                                           &LocalStrings.ansiString,
                                           FALSE
                                         );
    if (!NT_SUCCESS( status )) {

        DbgPrint( "IOINIT: AnsiToUnicode( %Z ) failed - %x\n", &LocalStrings.ansiString, status );

        HeadlessKernelAddLogEntry(HEADLESS_LOG_ANSI_TO_UNICODE_FAILED, NULL);
        IopInitFailCode = 12;
        return FALSE;
    }

    //
    // Free local strings
    //

    ExFreePool( LocalStrings.ansiString.Buffer );
    ExFreePool( ntDeviceName.Buffer );

    //
    // Also restore the NT Global Flags to their original state.
    //

    NtGlobalFlag = oldNtGlobalFlag;

    //
    // Let WMI have a second chance to initialize, now that all drivers
    // are started and should be ready to get WMI irps
    //
    WMIInitialize(1, NULL);

    //
    // Call Power manager to initialize for post-boot drivers
    //
    PoInitDriverServices(1);

    //
    // Indicate that the I/O system successfully initialized itself.
    //

    return TRUE;

}

VOID
IopSetIoRoutines()
{
    if (pIofCompleteRequest == NULL) {

        pIofCompleteRequest = IopfCompleteRequest;
    }

    if (pIoAllocateIrp == NULL) {

        pIoAllocateIrp = IopAllocateIrpPrivate;
    }

    if (pIoFreeIrp == NULL) {

        pIoFreeIrp = IopFreeIrp;
    }
}

NTSTATUS
IopFetchConfigurationInformation(
    IN OUT  PWSTR   *ListTarget,
    IN      GUID    ClassGuid,
    IN      ULONG   ExpectedNumber,
    OUT     ULONG   *NumberFound
)
{
    NTSTATUS    status;
    ULONG       count = 0;
    wchar_t *   pNameList;

    //
    // ask PNP to give us a list with all the currently active disks
    //

    pNameList = *ListTarget;

    status = IoGetDeviceInterfaces(&ClassGuid, NULL, 0, ListTarget);

    if (!NT_SUCCESS(status)) {
        if (pNameList) {
            *pNameList = L'\0';
        }
        return STATUS_UNSUCCESSFUL;
    } else {

        //
        // count the number of disks returned
        //

        pNameList = *ListTarget;
        while (*pNameList != L'\0') {

            count++;
            pNameList = pNameList + (wcslen(pNameList) + 1);

        }

        pNameList = *ListTarget;

        //
        // if the disk returned by PNP are not all the disks in the system
        // it means that some legacy driver has generated a disk device object/link.
        // In that case we need to enumerate all pnp disks and then using the legacy
        // for-loop also enumerate the non-pnp disks
        //

        *NumberFound = count;

        if ( *NumberFound < ExpectedNumber ) {
            return STATUS_UNSUCCESSFUL;
        }

    }

    return STATUS_SUCCESS;
}

NTSTATUS
IopCreateArcNamesDisk(
    IN  PLOADER_PARAMETER_BLOCK LoaderBlock,
    IN  BOOLEAN                 SingleBiosDiskFound,
    OUT BOOLEAN                 *BootDiskFound
    )
/*++

Routine Description:

    Helper routine to create Arc Names for disk devices.
    See IopCreateArcNames for more details.

Arguments:

    LoaderBlock - Supplies a pointer to the loader parameter block that was
        created by the OS Loader.

    SingleBiosDiskFound - Determines whether there is only one disk to process.

    BootDiskFound - TRUE if we find the boot disk and create it's arc name.

Return Value:

    STATUS_INSUFFICIENT_RESOURCES if we can't allocate memory, otherwise
    SUCCESS.

--*/
{
    STRING arcBootDeviceString;
    CHAR deviceNameBuffer[128];
    STRING deviceNameString;
    UNICODE_STRING deviceNameUnicodeString;
    PDEVICE_OBJECT deviceObject;
    CHAR arcNameBuffer[128];
    STRING arcNameString;
    UNICODE_STRING arcNameUnicodeString;
    PFILE_OBJECT fileObject;
    NTSTATUS status;
    IO_STATUS_BLOCK ioStatusBlock;
    DISK_GEOMETRY diskGeometry;
    PDRIVE_LAYOUT_INFORMATION_EX driveLayout;
    PLIST_ENTRY listEntry;
    PARC_DISK_SIGNATURE diskBlock;
    ULONG diskNumber;
    ULONG partitionNumber;
    PCHAR arcName;
    PULONG buffer;
    PIRP irp;
    KEVENT event;
    LARGE_INTEGER offset;
    ULONG checkSum;
    SIZE_T i;
    PVOID tmpPtr;
    BOOLEAN useLegacyEnumerationDisk = FALSE;
    PARC_DISK_INFORMATION arcInformation = LoaderBlock->ArcDiskInformation;
    ULONG totalDriverDisksFound = IoGetConfigurationInformation()->DiskCount;
    ULONG totalPnpDisksFound = 0;
    STRING arcSystemDeviceString;
    STRING osLoaderPathString;
    UNICODE_STRING osLoaderPathUnicodeString;
    PWSTR diskList = NULL;
    wchar_t *pDiskNameList;
    STORAGE_DEVICE_NUMBER   pnpDiskDeviceNumber;
    ULONG  diskSignature;

    //
    // ask PNP to give us a list with all the currently active disks
    //

    pnpDiskDeviceNumber.DeviceNumber = 0xFFFFFFFF;

    status = IopFetchConfigurationInformation( &diskList,
                                               DiskClassGuid,
                                               totalDriverDisksFound,
                                               &totalPnpDisksFound );

    if (!NT_SUCCESS(status)) {
        useLegacyEnumerationDisk = TRUE;
    }

    pDiskNameList = diskList;


    //
    // Get ARC boot & system device name from loader block.
    //

    RtlInitAnsiString( &arcBootDeviceString,
                       LoaderBlock->ArcBootDeviceName );

    RtlInitAnsiString( &arcSystemDeviceString,
                       LoaderBlock->ArcHalDeviceName );

    //
    // For each disk in the system do the following:
    // 1. open the device
    // 2. get its geometry
    // 3. read the MBR
    // 4. determine ARC name via disk signature and checksum
    // 5. construct ARC name.
    // In order to deal with the case of disk dissappearing before we get to this point
    // (due to a failed start on one of many disks present in the system) we ask PNP for a list
    // of all the currenttly active disks in the system. If the number of disks returned is
    // less than the IoGetConfigurationInformation()->DiskCount, then we have legacy disks
    // that we need to enumerate in the for loop.
    // In the legacy case, the ending condition for the loop is NOT the total disk on the
    // system but an arbitrary number of the max total legacy disks expected in the system..
    // Additional note: Legacy disks get assigned symbolic links AFTER all pnp enumeration is complete
    //

    totalDriverDisksFound = max(totalPnpDisksFound,totalDriverDisksFound);

    if (useLegacyEnumerationDisk && (totalPnpDisksFound == 0)) {

        //
        // search up to a maximum arbitrary number of legacy disks
        //

        totalDriverDisksFound +=20;
    }

    for (diskNumber = 0;
         diskNumber < totalDriverDisksFound;
         diskNumber++)
        {

        //
        // Construct the NT name for a disk and obtain a reference.
        //

        if (pDiskNameList && (*pDiskNameList != L'\0')) {

            //
            // retrieve the first symbolic linkname from the PNP disk list
            //

            RtlInitUnicodeString(&deviceNameUnicodeString, pDiskNameList);
            pDiskNameList = pDiskNameList + (wcslen(pDiskNameList) + 1);

            status = IoGetDeviceObjectPointer( &deviceNameUnicodeString,
                                               FILE_READ_ATTRIBUTES,
                                               &fileObject,
                                               &deviceObject );

            if (NT_SUCCESS(status)) {

                //
                // since PNP gave us just a sym link we have to retrieve the actual
                // disk number through an IOCTL call to the disk stack.
                // Create IRP for get device number device control.
                //

                irp = IoBuildDeviceIoControlRequest( IOCTL_STORAGE_GET_DEVICE_NUMBER,
                                                     deviceObject,
                                                     NULL,
                                                     0,
                                                     &pnpDiskDeviceNumber,
                                                     sizeof(STORAGE_DEVICE_NUMBER),
                                                     FALSE,
                                                     &event,
                                                     &ioStatusBlock );
                if (!irp) {
                    ObDereferenceObject( fileObject );
                    if (diskList) {
                        ExFreePool(diskList);
                    }
                    return STATUS_INSUFFICIENT_RESOURCES;
                }

                KeInitializeEvent( &event,
                                   NotificationEvent,
                                   FALSE );
                status = IoCallDriver( deviceObject,
                                       irp );

                if (status == STATUS_PENDING) {
                    KeWaitForSingleObject( &event,
                                           Executive,
                                           KernelMode,
                                           FALSE,
                                           NULL );
                    status = ioStatusBlock.Status;
                }

                if (!NT_SUCCESS( status )) {
                    ObDereferenceObject( fileObject );
                    continue;
                }

            }

            if (useLegacyEnumerationDisk && (*pDiskNameList == L'\0') ) {

                //
                // end of pnp disks
                // if there are any legacy disks following we need to update
                // the total disk found number to cover the maximum disk number
                // a legacy disk could be at. (in a sparse name space)
                //

                if (pnpDiskDeviceNumber.DeviceNumber == 0xFFFFFFFF) {
                    pnpDiskDeviceNumber.DeviceNumber = 0;
                }

                diskNumber = max(diskNumber,pnpDiskDeviceNumber.DeviceNumber);
                totalDriverDisksFound = diskNumber + 20;

            }

        } else {

            sprintf( deviceNameBuffer,
                     "\\Device\\Harddisk%d\\Partition0",
                     diskNumber );
            
            RtlInitAnsiString( &deviceNameString, deviceNameBuffer );
            status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                                   &deviceNameString,
                                                   TRUE );

            if (!NT_SUCCESS( status )) {
                if (diskList) {
                    ExFreePool(diskList);
                }
                return status;
            }

            status = IoGetDeviceObjectPointer( &deviceNameUnicodeString,
                                               FILE_READ_ATTRIBUTES,
                                               &fileObject,
                                               &deviceObject );

            RtlFreeUnicodeString( &deviceNameUnicodeString );

            //
            // set the pnpDiskNumber value so its not used.
            //

            pnpDiskDeviceNumber.DeviceNumber = 0xFFFFFFFF;

        }


        if (!NT_SUCCESS( status )) {

            continue;
        }

        //
        // Create IRP for get drive geometry device control.
        //

        irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_GET_DRIVE_GEOMETRY,
                                             deviceObject,
                                             NULL,
                                             0,
                                             &diskGeometry,
                                             sizeof(DISK_GEOMETRY),
                                             FALSE,
                                             &event,
                                             &ioStatusBlock );
        if (!irp) {
            ObDereferenceObject( fileObject );
            if (diskList) {
                ExFreePool(diskList);
            }
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        KeInitializeEvent( &event,
                           NotificationEvent,
                           FALSE );
        status = IoCallDriver( deviceObject,
                               irp );

        if (status == STATUS_PENDING) {
            KeWaitForSingleObject( &event,
                                   Executive,
                                   KernelMode,
                                   FALSE,
                                   NULL );
            status = ioStatusBlock.Status;
        }

        if (!NT_SUCCESS( status )) {
            ObDereferenceObject( fileObject );
            continue;
        }

        //
        // Get partition information for this disk.
        //


        status = IoReadPartitionTableEx(deviceObject,
                                       &driveLayout );


        if (!NT_SUCCESS( status )) {
            ObDereferenceObject( fileObject );
            continue;
        }


        //
        // Make sure sector size is at least 512 bytes.
        //

        if (diskGeometry.BytesPerSector < 512) {
            diskGeometry.BytesPerSector = 512;
        }

        //
        // Check to see if EZ Drive is out there on this disk.  If
        // it is then zero out the signature in the drive layout since
        // this will never be written by anyone AND change to offset to
        // actually read sector 1 rather than 0 cause that's what the
        // loader actually did.
        //

        offset.QuadPart = 0;
        HalExamineMBR( deviceObject,
                       diskGeometry.BytesPerSector,
                       (ULONG)0x55,
                       &tmpPtr );

        if (tmpPtr) {

            offset.QuadPart = diskGeometry.BytesPerSector;
            ExFreePool(tmpPtr);
        }

        //
        // Allocate buffer for sector read and construct the read request.
        //

        buffer = ExAllocatePool( NonPagedPoolCacheAligned,
                                 diskGeometry.BytesPerSector );

        if (buffer) {
            irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
                                                deviceObject,
                                                buffer,
                                                diskGeometry.BytesPerSector,
                                                &offset,
                                                &event,
                                                &ioStatusBlock );

            if (!irp) {
                ExFreePool(driveLayout);
                ExFreePool(buffer);
                ObDereferenceObject( fileObject );
                if (diskList) {
                    ExFreePool(diskList);
                }
                return STATUS_INSUFFICIENT_RESOURCES;
            }
        } else {
            ExFreePool(driveLayout);
            ObDereferenceObject( fileObject );
            if (diskList) {
                ExFreePool(diskList);
            }
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        KeInitializeEvent( &event,
                           NotificationEvent,
                           FALSE );
        status = IoCallDriver( deviceObject,
                               irp );
        if (status == STATUS_PENDING) {
            KeWaitForSingleObject( &event,
                                   Executive,
                                   KernelMode,
                                   FALSE,
                                   NULL );
            status = ioStatusBlock.Status;
        }

        if (!NT_SUCCESS( status )) {
            ExFreePool(driveLayout);
            ExFreePool(buffer);
            ObDereferenceObject( fileObject );
            continue;
        }

        ObDereferenceObject( fileObject );

        //
        // Calculate MBR sector checksum.  Only 512 bytes are used.
        //

        checkSum = 0;
        for (i = 0; i < 128; i++) {
            checkSum += buffer[i];
        }

        //
        // For each ARC disk information record in the loader block
        // match the disk signature and checksum to determine its ARC
        // name and construct the NT ARC names symbolic links.
        //

        for (listEntry = arcInformation->DiskSignatures.Flink;
             listEntry != &arcInformation->DiskSignatures;
             listEntry = listEntry->Flink) {

            //
            // Get next record and compare disk signatures.
            //

            diskBlock = CONTAINING_RECORD( listEntry,
                                           ARC_DISK_SIGNATURE,
                                           ListEntry );

            //
            // Compare disk signatures.
            //
            // Or if there is only a single disk drive from
            // both the bios and driver viewpoints then
            // assign an arc name to that drive.
            //



            if ((SingleBiosDiskFound &&
                 (totalDriverDisksFound == 1) &&
                 (driveLayout->PartitionStyle == PARTITION_STYLE_MBR)) ||

                (IopVerifyDiskSignature(driveLayout, diskBlock, &diskSignature) &&
                 !(diskBlock->CheckSum + checkSum))) {

                //
                // Create unicode device name for physical disk.
                //

                status = STATUS_SUCCESS;

                if (pnpDiskDeviceNumber.DeviceNumber == 0xFFFFFFFF) {

                    sprintf( deviceNameBuffer,
                             "\\Device\\Harddisk%d\\Partition0",
                             diskNumber );
                } else {

                    sprintf( deviceNameBuffer,
                             "\\Device\\Harddisk%d\\Partition0",
                             pnpDiskDeviceNumber.DeviceNumber );
                }

                RtlInitAnsiString( &deviceNameString, deviceNameBuffer );
                status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                                       &deviceNameString,
                                                       TRUE );
                if (!NT_SUCCESS( status )) {
                    ExFreePool( driveLayout );
                    ExFreePool( buffer );
                    if (diskList) {
                        ExFreePool(diskList);
                    }
                    return status;
                }

                //
                // Create unicode ARC name for this partition.
                //

                arcName = diskBlock->ArcName;

                sprintf( arcNameBuffer,
                         "\\ArcName\\%s",
                         arcName );

                RtlInitAnsiString( &arcNameString, arcNameBuffer );
                status = RtlAnsiStringToUnicodeString( &arcNameUnicodeString,
                                                       &arcNameString,
                                                       TRUE );
                if (!NT_SUCCESS( status )) {
                    ExFreePool( driveLayout );
                    ExFreePool( buffer );
                    RtlFreeUnicodeString( &deviceNameUnicodeString );
                    if (diskList) {
                        ExFreePool(diskList);
                    }
                    return status;
                }

                //
                // Create symbolic link between NT device name and ARC name.
                //

                IoCreateSymbolicLink( &arcNameUnicodeString,
                                      &deviceNameUnicodeString );
                RtlFreeUnicodeString( &arcNameUnicodeString );
                RtlFreeUnicodeString( &deviceNameUnicodeString );

                //
                // Create an ARC name for every partition on this disk.
                //

                for (partitionNumber = 0;
                     partitionNumber < driveLayout->PartitionCount;
                     partitionNumber++) {

                    //
                    // Create unicode NT device name.
                    //

                    if (pnpDiskDeviceNumber.DeviceNumber == 0xFFFFFFFF) {

                        sprintf( deviceNameBuffer,
                                 "\\Device\\Harddisk%d\\Partition%d",
                                 diskNumber,
                                 partitionNumber+1 );

                    } else {

                        sprintf( deviceNameBuffer,
                                 "\\Device\\Harddisk%d\\Partition%d",
                                 pnpDiskDeviceNumber.DeviceNumber,
                                 partitionNumber+1 );

                    }

                    RtlInitAnsiString( &deviceNameString, deviceNameBuffer );
                    status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                                           &deviceNameString,
                                                           TRUE );
                    if (!NT_SUCCESS( status )) {
                        ExFreePool( driveLayout );
                        ExFreePool( buffer );
                        if (diskList) {
                            ExFreePool(diskList);
                        }
                        return status;
                    }

                    //
                    // Create unicode ARC name for this partition and
                    // check to see if this is the boot disk.
                    //

                    sprintf( arcNameBuffer,
                             "%spartition(%d)",
                             arcName,
                             partitionNumber+1);

                    RtlInitAnsiString( &arcNameString, arcNameBuffer );
                    if (RtlEqualString( &arcNameString,
                                        &arcBootDeviceString,
                                        TRUE )) {
                        *BootDiskFound = TRUE;
                    }

                    //
                    // See if this is the system partition.
                    //
                    if (RtlEqualString( &arcNameString,
                                        &arcSystemDeviceString,
                                        TRUE )) {
                        //
                        // We've found the system partition--store it away in the registry
                        // to later be transferred to a application-friendly location.
                        //
                        RtlInitAnsiString( &osLoaderPathString, LoaderBlock->NtHalPathName );
                        status = RtlAnsiStringToUnicodeString( &osLoaderPathUnicodeString,
                                                               &osLoaderPathString,
                                                               TRUE );

                        if (!NT_SUCCESS( status )) {
                            ExFreePool( driveLayout );
                            ExFreePool( buffer );
                            if (diskList) {
                                ExFreePool(diskList);
                            }
                            RtlFreeUnicodeString( &deviceNameUnicodeString );
#if DBG
                            DbgPrint("IopCreateArcNames: couldn't allocate unicode string for OsLoader path - %x\n", status);
#endif // DBG
                            return status;
                        }

                        IopStoreSystemPartitionInformation( &deviceNameUnicodeString,
                                                                &osLoaderPathUnicodeString );

                        RtlFreeUnicodeString( &osLoaderPathUnicodeString );
                    }

                    //
                    // Add the NT ARC namespace prefix to the ARC name constructed.
                    //

                    sprintf( arcNameBuffer,
                             "\\ArcName\\%spartition(%d)",
                             arcName,
                             partitionNumber+1 );

                    RtlInitAnsiString( &arcNameString, arcNameBuffer );
                    status = RtlAnsiStringToUnicodeString( &arcNameUnicodeString,
                                                           &arcNameString,
                                                           TRUE );
                    if (!NT_SUCCESS( status )) {
                        ExFreePool( driveLayout );
                        ExFreePool( buffer );
                        RtlFreeUnicodeString( &deviceNameUnicodeString );
                        if (diskList) {
                            ExFreePool(diskList);
                        }
                        return status;
                    }

                    //
                    // Create symbolic link between NT device name and ARC name.
                    //

                    IoCreateSymbolicLink( &arcNameUnicodeString,
                                          &deviceNameUnicodeString );
                    RtlFreeUnicodeString( &arcNameUnicodeString );
                    RtlFreeUnicodeString( &deviceNameUnicodeString );
                }

            } else {

#if DBG
                //
                // Check key indicators to see if this condition may be
                // caused by a viral infection.
                //

                if (diskBlock->Signature == diskSignature &&
                    (diskBlock->CheckSum + checkSum) != 0 &&
                    diskBlock->ValidPartitionTable) {
                    DbgPrint("IopCreateArcNames: Virus or duplicate disk signatures\n");
                }
#endif
            }
        }

        ExFreePool( driveLayout );
        ExFreePool( buffer );
    }

    if (diskList) {
        ExFreePool(diskList);
    }

    return STATUS_SUCCESS;
}

NTSTATUS
IopCreateArcNamesCd(
    IN PLOADER_PARAMETER_BLOCK LoaderBlock
    )
/*++

Routine Description:

    Helper routine to create Arc Names for CD devices.
    See IopCreateArcNames for more details.

Arguments:

    LoaderBlock - Supplies a pointer to the loader parameter block that was
        created by the OS Loader.

Return Value:

    STATUS_INSUFFICIENT_RESOURCES if we can't allocate memory, otherwise
    SUCCESS.

--*/
{
    CHAR deviceNameBuffer[128];
    STRING deviceNameString;
    UNICODE_STRING deviceNameUnicodeString;
    PDEVICE_OBJECT deviceObject;
    CHAR arcNameBuffer[128];
    STRING arcNameString;
    UNICODE_STRING arcNameUnicodeString;
    PFILE_OBJECT fileObject;
    NTSTATUS status;
    IO_STATUS_BLOCK ioStatusBlock;
    PLIST_ENTRY listEntry;
    PARC_DISK_SIGNATURE diskBlock;
    ULONG diskNumber;
    PULONG buffer;
    PIRP irp;
    KEVENT event;
    LARGE_INTEGER offset;
    ULONG checkSum;
    SIZE_T i;
    BOOLEAN useLegacyEnumerationCdRom = FALSE;
    PARC_DISK_INFORMATION arcInformation = LoaderBlock->ArcDiskInformation;
    ULONG currentDiskNumber = 0;
    ULONG totalPnpCdRomsFound = 0;
    ULONG totalDriverCdRomsFound = IoGetConfigurationInformation()->CdRomCount;
    PWSTR cdRomList = NULL;
    wchar_t *pCdRomNameList;
    STORAGE_DEVICE_NUMBER   pnpDiskDeviceNumber;

    //
    // ask PNP to give us a list with all the currently active disks
    //

    status = IopFetchConfigurationInformation( &cdRomList,
                                               CdRomClassGuid,
                                               totalDriverCdRomsFound,
                                               &totalPnpCdRomsFound );

    if (!NT_SUCCESS(status)) {
        useLegacyEnumerationCdRom = TRUE;
    }

    pCdRomNameList = cdRomList;

    //
    // Locate the disk block that represents the boot device.
    //

    diskBlock = NULL;
    for (listEntry = arcInformation->DiskSignatures.Flink;
         listEntry != &arcInformation->DiskSignatures;
         listEntry = listEntry->Flink) {

        diskBlock = CONTAINING_RECORD( listEntry,
                                       ARC_DISK_SIGNATURE,
                                       ListEntry );
        if (strcmp( diskBlock->ArcName, LoaderBlock->ArcBootDeviceName ) == 0) {
            break;
        }
        diskBlock = NULL;
    }

    if (diskBlock) {

        //
        // This could be a CdRom boot.  Search all of the NT CdRoms
        // to locate a checksum match on the diskBlock found.  If
        // there is a match, assign the ARC name to the CdRom.
        //

        irp = NULL;
        buffer = ExAllocatePool( NonPagedPoolCacheAligned,
                                 2048 );
        if (buffer) {

            //
            // Construct the NT names for CdRoms and search each one
            // for a checksum match.  If found, create the ARC Name
            // symbolic link.
            //

            totalDriverCdRomsFound = max(totalPnpCdRomsFound,totalDriverCdRomsFound);
            currentDiskNumber = 0;

            if (useLegacyEnumerationCdRom && (totalPnpCdRomsFound == 0)){

                //
                // search up to a maximum arbitrary number of legacy CdRoms
                //

                totalDriverCdRomsFound += 5;
            }

            for (diskNumber = 0;
                 diskNumber < totalDriverCdRomsFound;
                 diskNumber++) {

                //
                // First try checking any PNP enumerated CdRoms
                //
                if ( pCdRomNameList && (*pCdRomNameList != L'\0')) {
                    RtlInitUnicodeString( &deviceNameUnicodeString, pCdRomNameList);
                    pCdRomNameList = pCdRomNameList + (wcslen(pCdRomNameList) + 1 );
                    status = IoGetDeviceObjectPointer( &deviceNameUnicodeString,
                                                       FILE_READ_ATTRIBUTES,
                                                       &fileObject,
                                                       &deviceObject );

                    if ( !NT_SUCCESS( status )) {
                        if ( cdRomList) {
                            ExFreePool(cdRomList);
                        }
                        ExFreePool( buffer );
                        return status;
                    }

                    //
                    // since PNP gave us just a sym link we have to retrieve the actual
                    // disk number through an IOCTL call to the disk stack.
                    // Create IRP for get device number device control.
                    //

                    irp = IoBuildDeviceIoControlRequest( IOCTL_STORAGE_GET_DEVICE_NUMBER,
                                                         deviceObject,
                                                         NULL,
                                                         0,
                                                         &pnpDiskDeviceNumber,
                                                         sizeof(STORAGE_DEVICE_NUMBER),
                                                         FALSE,
                                                         &event,
                                                         &ioStatusBlock );
                    if (!irp) {
                        if ( cdRomList) {
                            ExFreePool(cdRomList);
                        }
                        ExFreePool( buffer );
                        return STATUS_INSUFFICIENT_RESOURCES;
                    }

                    KeInitializeEvent( &event,
                                       NotificationEvent,
                                       FALSE );
                    status = IoCallDriver( deviceObject,
                                           irp );

                    if (status == STATUS_PENDING) {
                        KeWaitForSingleObject( &event,
                                               Executive,
                                               KernelMode,
                                               FALSE,
                                               NULL );
                        status = ioStatusBlock.Status;
                    }

                    if (!NT_SUCCESS( status )) {
                        if ( cdRomList) {
                            ExFreePool(cdRomList);
                        }
                        ExFreePool( buffer );
                        return status;
                    }

                    sprintf( deviceNameBuffer,
                             "\\Device\\CdRom%d",
                             pnpDiskDeviceNumber.DeviceNumber );
                    
                    RtlInitAnsiString( &deviceNameString, deviceNameBuffer );
                    status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                                           &deviceNameString,
                                                           TRUE );

                    if (!NT_SUCCESS( status )) {
                        if ( cdRomList) {
                            ExFreePool(cdRomList);
                        }
                        ExFreePool( buffer );
                        return status;
                    }


                //
                // Fall back to legacy devices if we have no more PNP
                //
                } else {
                    sprintf( deviceNameBuffer,
                             "\\Device\\CdRom%d",
                             currentDiskNumber );
                    
                    currentDiskNumber += 1;
                    RtlInitAnsiString( &deviceNameString, deviceNameBuffer );
                    status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                                           &deviceNameString,
                                                           TRUE );

                    if (NT_SUCCESS( status )) {
                        status = IoGetDeviceObjectPointer( &deviceNameUnicodeString,
                                                           FILE_READ_ATTRIBUTES,
                                                           &fileObject,
                                                           &deviceObject );
                        if (!NT_SUCCESS( status )) {

                            //
                            // All CdRoms have been processed.
                            //

                            RtlFreeUnicodeString( &deviceNameUnicodeString );
                            break;
                        }
                    } else {
                        if (cdRomList) {
                            ExFreePool(cdRomList);
                        }
                        ExFreePool(buffer);
                        return STATUS_INSUFFICIENT_RESOURCES;
                    }

                }

                //
                // Read the block for the checksum calculation.
                //


                offset.QuadPart = 0x8000;
                irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
                                                    deviceObject,
                                                    buffer,
                                                    2048,
                                                    &offset,
                                                    &event,
                                                    &ioStatusBlock );
                checkSum = 0;
                if (irp) {
                    KeInitializeEvent( &event,
                                       NotificationEvent,
                                       FALSE );
                    status = IoCallDriver( deviceObject,
                                           irp );
                    if (status == STATUS_PENDING) {
                        KeWaitForSingleObject( &event,
                                               Executive,
                                               KernelMode,
                                               FALSE,
                                               NULL );
                        status = ioStatusBlock.Status;
                    }

                    if (NT_SUCCESS( status )) {

                        //
                        // Calculate MBR sector checksum.
                        // 2048 bytes are used.
                        //

                        for (i = 0; i < 2048 / sizeof(ULONG) ; i++) {
                            checkSum += buffer[i];
                        }
                    }
                }
                ObDereferenceObject( fileObject );

                if (!(diskBlock->CheckSum + checkSum)) {

                    //
                    // This is the boot CdRom.  Create the symlink for
                    // the ARC name from the loader block.
                    //

                    sprintf( arcNameBuffer,
                             "\\ArcName\\%s",
                             LoaderBlock->ArcBootDeviceName );

                    RtlInitAnsiString( &arcNameString, arcNameBuffer );
                    status = RtlAnsiStringToUnicodeString( &arcNameUnicodeString,
                                                           &arcNameString,
                                                           TRUE );

                    if (NT_SUCCESS( status )) {

                        IoCreateSymbolicLink( &arcNameUnicodeString,
                                              &deviceNameUnicodeString );
                        RtlFreeUnicodeString( &arcNameUnicodeString );
                    } else {
                        ExFreePool(buffer);
                        if (cdRomList) {
                            ExFreePool(cdRomList);
                        }
                        RtlFreeUnicodeString( &deviceNameUnicodeString );
                        return status;
                    }

                    RtlFreeUnicodeString( &deviceNameUnicodeString );
                    break;
                }
                RtlFreeUnicodeString( &deviceNameUnicodeString );
            }
            ExFreePool(buffer);
        }
    }

    if (cdRomList) {
        ExFreePool(cdRomList);
    }
    return STATUS_SUCCESS;
}

NTSTATUS
IopCreateArcNames(
    IN PLOADER_PARAMETER_BLOCK LoaderBlock
    )

/*++

Routine Description:

    The loader block contains a table of disk signatures and corresponding
    ARC names. Each device that the loader can access will appear in the
    table. This routine opens each disk device in the system, reads the
    signature and compares it to the table. For each match, it creates a
    symbolic link between the nt device name and the ARC name.

    The checksum value provided by the loader is the ULONG sum of all
    elements in the checksum, inverted, plus 1:
    checksum = ~sum + 1;
    This way the sum of all of the elements can be calculated here and
    added to the checksum in the loader block.  If the result is zero, then
    there is a match.

Arguments:

    LoaderBlock - Supplies a pointer to the loader parameter block that was
        created by the OS Loader.

Return Value:

    None.

--*/

{
    STRING deviceNameString;
    UNICODE_STRING deviceNameUnicodeString;
    CHAR arcNameBuffer[128];
    STRING arcNameString;
    UNICODE_STRING arcNameUnicodeString;
    NTSTATUS status;
    SIZE_T i;
    BOOLEAN singleBiosDiskFound;
    BOOLEAN bootDiskFound = FALSE;
    PARC_DISK_INFORMATION arcInformation = LoaderBlock->ArcDiskInformation;
    STRING arcSystemDeviceString;
    STRING osLoaderPathString;
    UNICODE_STRING osLoaderPathUnicodeString;

    //
    // If a single bios disk was found if there is only a
    // single entry on the disk signature list.
    //

    singleBiosDiskFound = (arcInformation->DiskSignatures.Flink->Flink ==
                           &arcInformation->DiskSignatures) ? (TRUE) : (FALSE);


    //
    // Create hal/loader partition name
    //

    sprintf( arcNameBuffer, "\\ArcName\\%s", LoaderBlock->ArcHalDeviceName );
    RtlInitAnsiString( &arcNameString, arcNameBuffer );
    RtlAnsiStringToUnicodeString (&IoArcHalDeviceName, &arcNameString, TRUE);

    //
    // Create boot partition name
    //

    sprintf( arcNameBuffer, "\\ArcName\\%s", LoaderBlock->ArcBootDeviceName );
    RtlInitAnsiString( &arcNameString, arcNameBuffer );
    RtlAnsiStringToUnicodeString (&IoArcBootDeviceName, &arcNameString, TRUE);
    i = strlen (LoaderBlock->ArcBootDeviceName) + 1;
    IoLoaderArcBootDeviceName = ExAllocatePool (PagedPool, i);
    if (IoLoaderArcBootDeviceName) {
        memcpy (IoLoaderArcBootDeviceName, LoaderBlock->ArcBootDeviceName, i);
    }

    if (singleBiosDiskFound && strstr(LoaderBlock->ArcBootDeviceName, "cdrom")) {
        singleBiosDiskFound = FALSE;
    }

    //
    // Get ARC system device name from loader block.
    //

    RtlInitAnsiString( &arcSystemDeviceString,
                       LoaderBlock->ArcHalDeviceName );

    //
    // If this is a remote boot, create an ArcName for the redirector path.
    //

    if (IoRemoteBootClient) {

        bootDiskFound = TRUE;

        RtlInitAnsiString( &deviceNameString, "\\Device\\LanmanRedirector" );
        status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                               &deviceNameString,
                                               TRUE );

        if (NT_SUCCESS( status )) {

            sprintf( arcNameBuffer,
                     "\\ArcName\\%s",
                     LoaderBlock->ArcBootDeviceName );
            RtlInitAnsiString( &arcNameString, arcNameBuffer );
            status = RtlAnsiStringToUnicodeString( &arcNameUnicodeString,
                                                   &arcNameString,
                                                   TRUE );
            if (NT_SUCCESS( status )) {

                //
                // Create symbolic link between NT device name and ARC name.
                //

                IoCreateSymbolicLink( &arcNameUnicodeString,
                                      &deviceNameUnicodeString );
                RtlFreeUnicodeString( &arcNameUnicodeString );

                //
                // We've found the system partition--store it away in the registry
                // to later be transferred to a application-friendly location.
                //
                RtlInitAnsiString( &osLoaderPathString, LoaderBlock->NtHalPathName );
                status = RtlAnsiStringToUnicodeString( &osLoaderPathUnicodeString,
                                                       &osLoaderPathString,
                                                       TRUE );

                if (!NT_SUCCESS( status )) {
                    
                    RtlFreeUnicodeString( &deviceNameUnicodeString );
#if DBG
                    DbgPrint("IopCreateArcNames: couldn't allocate unicode string for OsLoader path - %x\n", status);
#endif // DBG
                    return status;
                }

                IopStoreSystemPartitionInformation( &deviceNameUnicodeString,
                                                    &osLoaderPathUnicodeString );

                RtlFreeUnicodeString( &osLoaderPathUnicodeString );
            }

            RtlFreeUnicodeString( &deviceNameUnicodeString );
        } else {
            return status;
        }
    }

    status = IopCreateArcNamesDisk( LoaderBlock,
                                    singleBiosDiskFound,
                                    &bootDiskFound );

    if (NT_SUCCESS(status) && !bootDiskFound) {
        
        status = IopCreateArcNamesCd( LoaderBlock );
    }

    return status;
}

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif // ALLOC_DATA_PRAGMA
const GENERIC_MAPPING IopFileMapping = {
    STANDARD_RIGHTS_READ |
        FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
    STANDARD_RIGHTS_WRITE |
        FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE,
    STANDARD_RIGHTS_EXECUTE |
        SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_EXECUTE,
    FILE_ALL_ACCESS
};

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif // ALLOC_DATA_PRAGMA
const GENERIC_MAPPING IopCompletionMapping = {
    STANDARD_RIGHTS_READ |
        IO_COMPLETION_QUERY_STATE,
    STANDARD_RIGHTS_WRITE |
        IO_COMPLETION_MODIFY_STATE,
    STANDARD_RIGHTS_EXECUTE |
        SYNCHRONIZE,
    IO_COMPLETION_ALL_ACCESS
};

BOOLEAN
IopCreateObjectTypes(
    VOID
    )

/*++

Routine Description:

    This routine creates the object types used by the I/O system and its
    components.  The object types created are:

        Adapter
        Controller
        Device
        Driver
        File
        I/O Completion

Arguments:

    None.

Return Value:

    The function value is a BOOLEAN indicating whether or not the object
    types were successfully created.


--*/

{
    OBJECT_TYPE_INITIALIZER objectTypeInitializer;
    UNICODE_STRING nameString;

    //
    // Initialize the common fields of the Object Type Initializer record
    //

    RtlZeroMemory( &objectTypeInitializer, sizeof( objectTypeInitializer ) );
    objectTypeInitializer.Length = sizeof( objectTypeInitializer );
    objectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
    objectTypeInitializer.GenericMapping = IopFileMapping;
    objectTypeInitializer.PoolType = NonPagedPool;
    objectTypeInitializer.ValidAccessMask = FILE_ALL_ACCESS;
    objectTypeInitializer.UseDefaultObject = TRUE;


    //
    // Create the object type for adapter objects.
    //

    RtlInitUnicodeString( &nameString, L"Adapter" );
    // objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( struct _ADAPTER_OBJECT );
    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoAdapterObjectType ))) {
        return FALSE;
    }

#ifdef _PNP_POWER_

    //
    // Create the object type for device helper objects.
    //

    RtlInitUnicodeString( &nameString, L"DeviceHandler" );
    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoDeviceHandlerObjectType ))) {
        return FALSE;
    }
    IoDeviceHandlerObjectSize = sizeof(DEVICE_HANDLER_OBJECT);

#endif

    //
    // Create the object type for controller objects.
    //

    RtlInitUnicodeString( &nameString, L"Controller" );
    objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( CONTROLLER_OBJECT );
    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoControllerObjectType ))) {
        return FALSE;
    }

    //
    // Create the object type for device objects.
    //

    RtlInitUnicodeString( &nameString, L"Device" );
    objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( DEVICE_OBJECT );
    objectTypeInitializer.ParseProcedure = IopParseDevice;
    objectTypeInitializer.CaseInsensitive = TRUE;
    objectTypeInitializer.DeleteProcedure = IopDeleteDevice;
    objectTypeInitializer.SecurityProcedure = IopGetSetSecurityObject;
    objectTypeInitializer.QueryNameProcedure = (OB_QUERYNAME_METHOD)NULL;
    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoDeviceObjectType ))) {
        return FALSE;
    }

    //
    // Create the object type for driver objects.
    //

    RtlInitUnicodeString( &nameString, L"Driver" );
    objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( DRIVER_OBJECT );
    objectTypeInitializer.ParseProcedure = (OB_PARSE_METHOD) NULL;
    objectTypeInitializer.DeleteProcedure = IopDeleteDriver;
    objectTypeInitializer.SecurityProcedure = (OB_SECURITY_METHOD) NULL;
    objectTypeInitializer.QueryNameProcedure = (OB_QUERYNAME_METHOD)NULL;


    //
    // This allows us to get a list of Driver objects.
    //
    if (IopVerifierOn) {
        objectTypeInitializer.MaintainTypeList = TRUE;
    }

    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoDriverObjectType ))) {
        return FALSE;
    }

    //
    // Create the object type for I/O completion objects.
    //

    RtlInitUnicodeString( &nameString, L"IoCompletion" );
    objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( KQUEUE );
    objectTypeInitializer.InvalidAttributes = OBJ_PERMANENT | OBJ_OPENLINK;
    objectTypeInitializer.GenericMapping = IopCompletionMapping;
    objectTypeInitializer.ValidAccessMask = IO_COMPLETION_ALL_ACCESS;
    objectTypeInitializer.DeleteProcedure = IopDeleteIoCompletion;
    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoCompletionObjectType ))) {
        return FALSE;
    }

    //
    // Create the object type for file objects.
    //

    RtlInitUnicodeString( &nameString, L"File" );
    objectTypeInitializer.DefaultPagedPoolCharge = IO_FILE_OBJECT_PAGED_POOL_CHARGE;
    objectTypeInitializer.DefaultNonPagedPoolCharge = IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE +
                                                      sizeof( FILE_OBJECT );
    objectTypeInitializer.InvalidAttributes = OBJ_PERMANENT | OBJ_EXCLUSIVE | OBJ_OPENLINK;
    objectTypeInitializer.GenericMapping = IopFileMapping;
    objectTypeInitializer.ValidAccessMask = FILE_ALL_ACCESS;
    objectTypeInitializer.MaintainHandleCount = TRUE;
    objectTypeInitializer.CloseProcedure = IopCloseFile;
    objectTypeInitializer.DeleteProcedure = IopDeleteFile;
    objectTypeInitializer.ParseProcedure = IopParseFile;
    objectTypeInitializer.SecurityProcedure = IopGetSetSecurityObject;
    objectTypeInitializer.QueryNameProcedure = IopQueryName;
    objectTypeInitializer.UseDefaultObject = FALSE;

    PERFINFO_MUNG_FILE_OBJECT_TYPE_INITIALIZER(objectTypeInitializer);

    if (!NT_SUCCESS( ObCreateObjectType( &nameString,
                                      &objectTypeInitializer,
                                      (PSECURITY_DESCRIPTOR) NULL,
                                      &IoFileObjectType ))) {
        return FALSE;
    }

    PERFINFO_UNMUNG_FILE_OBJECT_TYPE_INITIALIZER(objectTypeInitializer);

    return TRUE;
}

BOOLEAN
IopCreateRootDirectories(
    VOID
    )

/*++

Routine Description:

    This routine is invoked to create the object manager directory objects
    to contain the various device and file system driver objects.

Arguments:

    None.

Return Value:

    The function value is a BOOLEAN indicating whether or not the directory
    objects were successfully created.


--*/

{
    HANDLE handle;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING nameString;
    NTSTATUS status;

    //
    // Create the root directory object for the \Driver directory.
    //

    RtlInitUnicodeString( &nameString, L"\\Driver" );
    InitializeObjectAttributes( &objectAttributes,
                                &nameString,
                                OBJ_PERMANENT|OBJ_KERNEL_HANDLE,
                                (HANDLE) NULL,
                                (PSECURITY_DESCRIPTOR) NULL );

    status = NtCreateDirectoryObject( &handle,
                                      DIRECTORY_ALL_ACCESS,
                                      &objectAttributes );
    if (!NT_SUCCESS( status )) {
        return FALSE;
    } else {
        (VOID) ObCloseHandle( handle , KernelMode);
    }

    //
    // Create the root directory object for the \FileSystem directory.
    //

    RtlInitUnicodeString( &nameString, L"\\FileSystem" );

    status = NtCreateDirectoryObject( &handle,
                                      DIRECTORY_ALL_ACCESS,
                                      &objectAttributes );
    if (!NT_SUCCESS( status )) {
        return FALSE;
    } else {
        (VOID) ObCloseHandle( handle , KernelMode);
    }

    //
    // Create the root directory object for the \FileSystem\Filters directory.
    //

    RtlInitUnicodeString( &nameString, L"\\FileSystem\\Filters" );

    status = NtCreateDirectoryObject( &handle,
                                      DIRECTORY_ALL_ACCESS,
                                      &objectAttributes );
    if (!NT_SUCCESS( status )) {
        return FALSE;
    } else {
        (VOID) ObCloseHandle( handle , KernelMode);
    }

    return TRUE;
}

NTSTATUS
IopInitializeAttributesAndCreateObject(
    IN PUNICODE_STRING ObjectName,
    IN OUT POBJECT_ATTRIBUTES ObjectAttributes,
    OUT PDRIVER_OBJECT *DriverObject
    )

/*++

Routine Description:

    This routine is invoked to initialize a set of object attributes and
    to create a driver object.

Arguments:

    ObjectName - Supplies the name of the driver object.

    ObjectAttributes - Supplies a pointer to the object attributes structure
        to be initialized.

    DriverObject - Supplies a variable to receive a pointer to the resultant
        created driver object.

Return Value:

    The function value is the final status of the operation.

--*/

{
    NTSTATUS status;

    //
    // Simply initialize the object attributes and create the driver object.
    //

    InitializeObjectAttributes( ObjectAttributes,
                                ObjectName,
                                OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
                                (HANDLE) NULL,
                                (PSECURITY_DESCRIPTOR) NULL );

    status = ObCreateObject( KeGetPreviousMode(),
                             IoDriverObjectType,
                             ObjectAttributes,
                             KernelMode,
                             (PVOID) NULL,
                             (ULONG) (sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION )),
                             0,
                             0,
                             (PVOID *)DriverObject );
    return status;
}

NTSTATUS
IopInitializeBuiltinDriver(
    IN PUNICODE_STRING DriverName,
    IN PUNICODE_STRING RegistryPath,
    IN PDRIVER_INITIALIZE DriverInitializeRoutine,
    IN PKLDR_DATA_TABLE_ENTRY DriverEntry,
    IN BOOLEAN IsFilter,
    IN PDRIVER_OBJECT *Result
    )

/*++

Routine Description:

    This routine is invoked to initialize a built-in driver.

Arguments:

    DriverName - Specifies the name to be used in creating the driver object.

    RegistryPath - Specifies the path to be used by the driver to get to
        the registry.

    DriverInitializeRoutine - Specifies the initialization entry point of
        the built-in driver.

    DriverEntry - Specifies the driver data table entry to determine if the
        driver is a wdm driver.

Return Value:

    The function returns a pointer to a DRIVER_OBJECT if the built-in
    driver successfully initialized.  Otherwise, a value of NULL is returned.

--*/

{
    HANDLE handle;
    PDRIVER_OBJECT driverObject;
    PDRIVER_OBJECT tmpDriverObject;
    OBJECT_ATTRIBUTES objectAttributes;
    PWSTR buffer;
    NTSTATUS status;
    HANDLE serviceHandle;
    PWSTR pserviceName;
    USHORT serviceNameLength;
    PDRIVER_EXTENSION driverExtension;
    PIMAGE_NT_HEADERS ntHeaders;
    PVOID imageBase;
#if DBG
    LARGE_INTEGER stime, etime;
    ULONG dtime;
#endif
    PLIST_ENTRY entry;
    PKLDR_DATA_TABLE_ENTRY DataTableEntry;

    *Result = NULL;
    //
    // Log the file name
    //
    HeadlessKernelAddLogEntry(HEADLESS_LOG_LOADING_FILENAME, DriverName);

    //
    // Begin by creating the driver object.
    //

    status = IopInitializeAttributesAndCreateObject( DriverName,
                                                     &objectAttributes,
                                                     &driverObject );
    if (!NT_SUCCESS( status )) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_LOAD_FAILED, NULL);
        return status;
    }

    //
    // Initialize the driver object.
    //

    InitializeDriverObject( driverObject );
    driverObject->DriverInit = DriverInitializeRoutine;

    //
    // Insert the driver object into the object table.
    //

    status = ObInsertObject( driverObject,
                             NULL,
                             FILE_READ_DATA,
                             0,
                             (PVOID *) NULL,
                             &handle );

    if (!NT_SUCCESS( status )) {
        HeadlessKernelAddLogEntry(HEADLESS_LOG_LOAD_FAILED, NULL);
        return status;
    }

    //
    // Reference the handle and obtain a pointer to the driver object so that
    // the handle can be deleted without the object going away.
    //

    status = ObReferenceObjectByHandle( handle,
                                        0,
                                        IoDriverObjectType,
                                        KernelMode,
                                        (PVOID *) &tmpDriverObject,
                                        (POBJECT_HANDLE_INFORMATION) NULL );
    ASSERT(status == STATUS_SUCCESS);
    //
    // Fill in the DriverSection so the image will be automatically unloaded on
    // failures. We should use the entry from the PsModuleList.
    //

    entry = PsLoadedModuleList.Flink;
    while (entry != &PsLoadedModuleList && DriverEntry) {
        DataTableEntry = CONTAINING_RECORD(entry,
                                           KLDR_DATA_TABLE_ENTRY,
                                           InLoadOrderLinks);
        if (RtlEqualString((PSTRING)&DriverEntry->BaseDllName,
                    (PSTRING)&DataTableEntry->BaseDllName,
                    TRUE
                    )) {
            driverObject->DriverSection = DataTableEntry;
            break;
        }
        entry = entry->Flink;
    }

    //
    // The boot process takes a while loading drivers.   Indicate that
    // progress is being made.
    //

    InbvIndicateProgress();

    //
    // Get start and sice for the DriverObject.
    //

    if (DriverEntry) {
        imageBase = DriverEntry->DllBase;
        ntHeaders = RtlImageNtHeader(imageBase);
        driverObject->DriverStart = imageBase;
        driverObject->DriverSize = ntHeaders->OptionalHeader.SizeOfImage;
        if (!(ntHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_WDM_DRIVER)) {
            driverObject->Flags |= DRVO_LEGACY_DRIVER;
        }
    } else {
        ntHeaders = NULL;
        imageBase = NULL;
        driverObject->Flags |= DRVO_LEGACY_DRIVER;
    }

    //
    // Save the name of the driver so that it can be easily located by functions
    // such as error logging.
    //

    buffer = ExAllocatePool( PagedPool, DriverName->MaximumLength + 2 );

    if (buffer) {
        driverObject->DriverName.Buffer = buffer;
        driverObject->DriverName.MaximumLength = DriverName->MaximumLength;
        driverObject->DriverName.Length = DriverName->Length;

        RtlCopyMemory( driverObject->DriverName.Buffer,
                       DriverName->Buffer,
                       DriverName->MaximumLength );
        buffer[DriverName->Length >> 1] = (WCHAR) '\0';
    }

    //
    // Save the name of the service key so that it can be easily located by PnP
    // mamager.
    //

    driverExtension = driverObject->DriverExtension;
    if (RegistryPath && RegistryPath->Length != 0) {
        pserviceName = RegistryPath->Buffer + RegistryPath->Length / sizeof (WCHAR) - 1;
        if (*pserviceName == OBJ_NAME_PATH_SEPARATOR) {
            pserviceName--;
        }
        serviceNameLength = 0;
        while (pserviceName != RegistryPath->Buffer) {
            if (*pserviceName == OBJ_NAME_PATH_SEPARATOR) {
                pserviceName++;
                break;
            } else {
                serviceNameLength += sizeof(WCHAR);
                pserviceName--;
            }
        }
        if (pserviceName == RegistryPath->Buffer) {
            serviceNameLength += sizeof(WCHAR);
        }
        buffer = ExAllocatePool( NonPagedPool, serviceNameLength + sizeof(UNICODE_NULL) );

        if (buffer) {
            driverExtension->ServiceKeyName.Buffer = buffer;
            driverExtension->ServiceKeyName.MaximumLength = serviceNameLength + sizeof(UNICODE_NULL);
            driverExtension->ServiceKeyName.Length = serviceNameLength;

            RtlCopyMemory( driverExtension->ServiceKeyName.Buffer,
                           pserviceName,
                           serviceNameLength );
            buffer[driverExtension->ServiceKeyName.Length >> 1] = UNICODE_NULL;
        } else {
            status = STATUS_INSUFFICIENT_RESOURCES;
            driverExtension->ServiceKeyName.Buffer = NULL;
            driverExtension->ServiceKeyName.Length = 0;
            goto exit;
        }

        //
        // Prepare driver initialization
        //

        status = IopOpenRegistryKeyEx( &serviceHandle,
                                       NULL,
                                       RegistryPath,
                                       KEY_ALL_ACCESS
                                       );
        if (NT_SUCCESS(status)) {
            status = IopPrepareDriverLoading(&driverExtension->ServiceKeyName,
                                             serviceHandle,
                                             imageBase,
                                             IsFilter);
            NtClose(serviceHandle);
            if (!NT_SUCCESS(status)) {
                goto exit;
            }
        } else {
            goto exit;
        }
    } else {
        driverExtension->ServiceKeyName.Buffer = NULL;
        driverExtension->ServiceKeyName.MaximumLength = 0;
        driverExtension->ServiceKeyName.Length = 0;
    }

    //
    // Load the Registry information in the appropriate fields of the device
    // object.
    //

    driverObject->HardwareDatabase = &CmRegistryMachineHardwareDescriptionSystemName;

#if DBG
    KeQuerySystemTime (&stime);
#endif

    //
    // Now invoke the driver's initialization routine to initialize itself.
    //


    status = driverObject->DriverInit( driverObject, RegistryPath );


#if DBG

    //
    // If DriverInit took longer than 5 seconds or the driver did not load,
    // print a message.
    //

    KeQuerySystemTime (&etime);
    dtime  = (ULONG) ((etime.QuadPart - stime.QuadPart) / 1000000);

    if (dtime > 50  ||  !NT_SUCCESS( status )) {
        if (dtime < 10) {
            DbgPrint( "IOINIT: Built-in driver %wZ failed to initialize - %lX\n",
                DriverName, status );

        } else {
            DbgPrint( "IOINIT: Built-in driver %wZ took %d.%ds to ",
                DriverName, dtime/10, dtime%10 );

            if (NT_SUCCESS( status )) {
                DbgPrint ("initialize\n");
            } else {
                DbgPrint ("fail initialization - %lX\n", status);
            }
        }
    }
#endif
exit:

    NtClose( handle );

    if (NT_SUCCESS( status )) {
        IopReadyDeviceObjects( driverObject );
        HeadlessKernelAddLogEntry(HEADLESS_LOG_LOAD_SUCCESSFUL, NULL);
        *Result = driverObject;
        return status;
    } else {
        if (status != STATUS_PLUGPLAY_NO_DEVICE) {

            //
            // if STATUS_PLUGPLAY_NO_DEVICE, the driver was disable by hardware profile.
            //

            IopDriverLoadingFailed(NULL, &driverObject->DriverExtension->ServiceKeyName);
        }
        HeadlessKernelAddLogEntry(HEADLESS_LOG_LOAD_FAILED, NULL);
        ObMakeTemporaryObject(driverObject);
        ObDereferenceObject (driverObject);
        return status;
    }
}

BOOLEAN
IopMarkBootPartition(
    IN PLOADER_PARAMETER_BLOCK LoaderBlock
    )

/*++

Routine Description:

    This routine is invoked to locate and mark the boot partition device object
    as a boot device so that subsequent operations can fail more cleanly and
    with a better explanation of why the system failed to boot and run properly.

Arguments:

    LoaderBlock - Supplies a pointer to the loader parameter block created
        by the OS Loader during the boot process.  This structure contains
        the various system partition and boot device names and paths.

Return Value:

    The function value is TRUE if everything worked, otherwise FALSE.

Notes:

    If the boot partition device object cannot be found, then the system will
    bugcheck.

--*/

{
    OBJECT_ATTRIBUTES objectAttributes;
    STRING deviceNameString;
    CHAR deviceNameBuffer[256];
    UNICODE_STRING deviceNameUnicodeString;
    NTSTATUS status;
    HANDLE fileHandle;
    IO_STATUS_BLOCK ioStatus;
    PFILE_OBJECT fileObject;
    CHAR ArcNameFmt[12];

    ArcNameFmt[0] = '\\';
    ArcNameFmt[1] = 'A';
    ArcNameFmt[2] = 'r';
    ArcNameFmt[3] = 'c';
    ArcNameFmt[4] = 'N';
    ArcNameFmt[5] = 'a';
    ArcNameFmt[6] = 'm';
    ArcNameFmt[7] = 'e';
    ArcNameFmt[8] = '\\';
    ArcNameFmt[9] = '%';
    ArcNameFmt[10] = 's';
    ArcNameFmt[11] = '\0';
    //
    // Open the ARC boot device object. The boot device driver should have
    // created the object.
    //

    sprintf( deviceNameBuffer,
             ArcNameFmt,
             LoaderBlock->ArcBootDeviceName );

    RtlInitAnsiString( &deviceNameString, deviceNameBuffer );

    status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                           &deviceNameString,
                                           TRUE );

    if (!NT_SUCCESS( status )) {
        return FALSE;
    }

    InitializeObjectAttributes( &objectAttributes,
                                &deviceNameUnicodeString,
                                OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL );

    status = ZwOpenFile( &fileHandle,
                         FILE_READ_ATTRIBUTES,
                         &objectAttributes,
                         &ioStatus,
                         0,
                         FILE_NON_DIRECTORY_FILE );
    if (!NT_SUCCESS( status )) {
        KeBugCheckEx( INACCESSIBLE_BOOT_DEVICE,
                      (ULONG_PTR) &deviceNameUnicodeString,
                      status,
                      0,
                      0 );
    }

    //
    // Convert the file handle into a pointer to the device object itself.
    //

    status = ObReferenceObjectByHandle( fileHandle,
                                        0,
                                        IoFileObjectType,
                                        KernelMode,
                                        (PVOID *) &fileObject,
                                        NULL );
    if (!NT_SUCCESS( status )) {
        RtlFreeUnicodeString( &deviceNameUnicodeString );
        return FALSE;
    }

    //
    // Mark the device object represented by the file object.
    //

    fileObject->DeviceObject->Flags |= DO_SYSTEM_BOOT_PARTITION;

    //
    // Save away the characteristics of boot device object for later
    // use in WinPE mode
    //
    if (InitIsWinPEMode) {
        if (fileObject->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
            InitWinPEModeType |= INIT_WINPEMODE_REMOVABLE_MEDIA;
        }

        if (fileObject->DeviceObject->Characteristics & FILE_READ_ONLY_DEVICE) {
            InitWinPEModeType |= INIT_WINPEMODE_READONLY_MEDIA;
        }
    }

    //
    // Reference the device object and store the reference.
    //
    ObReferenceObject(fileObject->DeviceObject);

    IopErrorLogObject =  fileObject->DeviceObject;

    RtlFreeUnicodeString( &deviceNameUnicodeString );

    //
    // Finally, close the handle and dereference the file object.
    //

    ObCloseHandle( fileHandle, KernelMode);
    ObDereferenceObject( fileObject );

    return TRUE;
}

BOOLEAN
IopReassignSystemRoot(
    IN PLOADER_PARAMETER_BLOCK LoaderBlock,
    OUT PSTRING NtDeviceName
    )

/*++

Routine Description:

    This routine is invoked to reassign \SystemRoot from being an ARC path
    name to its NT path name equivalent.  This is done by looking up the
    ARC device name as a symbolic link and determining which NT device object
    is referred to by it.  The link is then replaced with the new name.

Arguments:

    LoaderBlock - Supplies a pointer to the loader parameter block created
        by the OS Loader during the boot process.  This structure contains
        the various system partition and boot device names and paths.

    NtDeviceName - Specifies a pointer to a STRING to receive the NT name of
        the device from which the system was booted.

Return Value:

    The function value is a BOOLEAN indicating whether or not the ARC name
    was resolved to an NT name.

--*/

{
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status;
    CHAR deviceNameBuffer[256];
    WCHAR arcNameUnicodeBuffer[64];
    CHAR arcNameStringBuffer[256];
    STRING deviceNameString;
    STRING arcNameString;
    STRING linkString;
    UNICODE_STRING linkUnicodeString;
    UNICODE_STRING deviceNameUnicodeString;
    UNICODE_STRING arcNameUnicodeString;
    HANDLE linkHandle;

#if DBG

    CHAR debugBuffer[256];
    STRING debugString;
    UNICODE_STRING debugUnicodeString;

#endif
    CHAR ArcNameFmt[12];

    ArcNameFmt[0] = '\\';
    ArcNameFmt[1] = 'A';
    ArcNameFmt[2] = 'r';
    ArcNameFmt[3] = 'c';
    ArcNameFmt[4] = 'N';
    ArcNameFmt[5] = 'a';
    ArcNameFmt[6] = 'm';
    ArcNameFmt[7] = 'e';
    ArcNameFmt[8] = '\\';
    ArcNameFmt[9] = '%';
    ArcNameFmt[10] = 's';
    ArcNameFmt[11] = '\0';

    //
    // Open the ARC boot device symbolic link object. The boot device
    // driver should have created the object.
    //

    sprintf( deviceNameBuffer,
             ArcNameFmt,
             LoaderBlock->ArcBootDeviceName );

    RtlInitAnsiString( &deviceNameString, deviceNameBuffer );

    status = RtlAnsiStringToUnicodeString( &deviceNameUnicodeString,
                                           &deviceNameString,
                                           TRUE );

    if (!NT_SUCCESS( status )) {
        return FALSE;
    }

    InitializeObjectAttributes( &objectAttributes,
                                &deviceNameUnicodeString,
                                OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL );

    status = NtOpenSymbolicLinkObject( &linkHandle,
                                       SYMBOLIC_LINK_ALL_ACCESS,
                                       &objectAttributes );

    if (!NT_SUCCESS( status )) {

#if DBG

        sprintf( debugBuffer, "IOINIT: unable to resolve %s, Status == %X\n",
                 deviceNameBuffer,
                 status );

        RtlInitAnsiString( &debugString, debugBuffer );

        status = RtlAnsiStringToUnicodeString( &debugUnicodeString,
                                               &debugString,
                                               TRUE );

        if (NT_SUCCESS( status )) {
            ZwDisplayString( &debugUnicodeString );
            RtlFreeUnicodeString( &debugUnicodeString );
        }

#endif // DBG

        RtlFreeUnicodeString( &deviceNameUnicodeString );
        return FALSE;
    }

    //
    // Get handle to \SystemRoot symbolic link.
    //

    arcNameUnicodeString.Buffer = arcNameUnicodeBuffer;
    arcNameUnicodeString.Length = 0;
    arcNameUnicodeString.MaximumLength = sizeof( arcNameUnicodeBuffer );

    status = NtQuerySymbolicLinkObject( linkHandle,
                                        &arcNameUnicodeString,
                                        NULL );

    if (!NT_SUCCESS( status )) {
        return FALSE;
    }

    arcNameString.Buffer = arcNameStringBuffer;
    arcNameString.Length = 0;
    arcNameString.MaximumLength = sizeof( arcNameStringBuffer );

    status = RtlUnicodeStringToAnsiString( &arcNameString,
                                           &arcNameUnicodeString,
                                           FALSE );

    arcNameStringBuffer[arcNameString.Length] = '\0';

    ObCloseHandle( linkHandle, KernelMode );
    RtlFreeUnicodeString( &deviceNameUnicodeString );

    RtlInitAnsiString( &linkString, INIT_SYSTEMROOT_LINKNAME );

    status = RtlAnsiStringToUnicodeString( &linkUnicodeString,
                                           &linkString,
                                           TRUE);

    if (!NT_SUCCESS( status )) {
        return FALSE;
    }

    InitializeObjectAttributes( &objectAttributes,
                                &linkUnicodeString,
                                OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL );

    status = NtOpenSymbolicLinkObject( &linkHandle,
                                       SYMBOLIC_LINK_ALL_ACCESS,
                                       &objectAttributes );

    if (!NT_SUCCESS( status )) {
        return FALSE;
    }

    NtMakeTemporaryObject( linkHandle );
    ObCloseHandle( linkHandle, KernelMode );

    sprintf( deviceNameBuffer,
             "%Z%s",
             &arcNameString,
             LoaderBlock->NtBootPathName );

    //
    // Get NT device name for \SystemRoot assignment.
    //

    RtlCopyString( NtDeviceName, &arcNameString );

    deviceNameBuffer[strlen(deviceNameBuffer)-1] = '\0';

    RtlInitAnsiString(&deviceNameString, deviceNameBuffer);

    InitializeObjectAttributes( &objectAttributes,
                                &linkUnicodeString,
                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT|OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL );

    status = RtlAnsiStringToUnicodeString( &arcNameUnicodeString,
                                           &deviceNameString,
                                           TRUE);

    if (!NT_SUCCESS( status )) {
        return FALSE;
    }

    status = NtCreateSymbolicLinkObject( &linkHandle,
                                         SYMBOLIC_LINK_ALL_ACCESS,
                                         &objectAttributes,
                                         &arcNameUnicodeString );

    RtlFreeUnicodeString( &arcNameUnicodeString );
    RtlFreeUnicodeString( &linkUnicodeString );
    ObCloseHandle( linkHandle, KernelMode );

#if DBG

    if (NT_SUCCESS( status )) {

        sprintf( debugBuffer,
                 "INIT: Reassigned %s => %s\n",
                 INIT_SYSTEMROOT_LINKNAME,
                 deviceNameBuffer );

    } else {

        sprintf( debugBuffer,
                 "INIT: unable to create %s => %s, Status == %X\n",
                 INIT_SYSTEMROOT_LINKNAME,
                 deviceNameBuffer,
                 status );
    }

    RtlInitAnsiString( &debugString, debugBuffer );

    status = RtlAnsiStringToUnicodeString( &debugUnicodeString,
                                           &debugString,
                                           TRUE );

    if (NT_SUCCESS( status )) {

        ZwDisplayString( &debugUnicodeString );
        RtlFreeUnicodeString( &debugUnicodeString );
    }

#endif // DBG

    return TRUE;
}

VOID
IopStoreSystemPartitionInformation(
    IN     PUNICODE_STRING NtSystemPartitionDeviceName,
    IN OUT PUNICODE_STRING OsLoaderPathName
    )

/*++

Routine Description:

    This routine writes two values to the registry (under HKLM\SYSTEM\Setup)--one
    containing the NT device name of the system partition and the other containing
    the path to the OS loader.  These values will later be migrated into a
    Win95-compatible registry location (NT path converted to DOS path), so that
    installation programs (including our own setup) have a rock-solid way of knowing
    what the system partition is, on both ARC and x86.

    ERRORS ENCOUNTERED IN THIS ROUTINE ARE NOT CONSIDERED FATAL.

Arguments:

    NtSystemPartitionDeviceName - supplies the NT device name of the system partition.
        This is the \Device\Harddisk<n>\Partition<m> name, which used to be the actual
        device name, but now is a symbolic link to a name of the form \Device\Volume<x>.
        We open up this symbolic link, and retrieve the name that it points to.  The
        target name is the one we store away in the registry.

    OsLoaderPathName - supplies the path (on the partition specified in the 1st parameter)
        where the OS loader is located.  Upon return, this path will have had its trailing
        backslash removed (if present, and path isn't root).

Return Value:

    None.

--*/

{
    NTSTATUS status;
    HANDLE linkHandle;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE systemHandle, setupHandle;
    UNICODE_STRING nameString, volumeNameString;
    WCHAR voumeNameStringBuffer[256];
    //
    // Declare a unicode buffer big enough to contain the longest string we'll be using.
    // (ANSI string in 'sizeof()' below on purpose--we want the number of chars here.)
    //
    WCHAR nameBuffer[sizeof("SystemPartition")];

    //
    // Both UNICODE_STRING buffers should be NULL-terminated.
    //

    ASSERT( NtSystemPartitionDeviceName->MaximumLength >= NtSystemPartitionDeviceName->Length + sizeof(WCHAR) );
    ASSERT( NtSystemPartitionDeviceName->Buffer[NtSystemPartitionDeviceName->Length / sizeof(WCHAR)] == L'\0' );

    ASSERT( OsLoaderPathName->MaximumLength >= OsLoaderPathName->Length + sizeof(WCHAR) );
    ASSERT( OsLoaderPathName->Buffer[OsLoaderPathName->Length / sizeof(WCHAR)] == L'\0' );

    //
    // Open the NtSystemPartitionDeviceName symbolic link, and find out the volume device
    // it points to.
    //

    InitializeObjectAttributes(&objectAttributes,
                               NtSystemPartitionDeviceName,
                               OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL
                              );

    status = NtOpenSymbolicLinkObject(&linkHandle,
                                      SYMBOLIC_LINK_QUERY,
                                      &objectAttributes
                                     );

    if (!NT_SUCCESS(status)) {
#if DBG
        DbgPrint("IopStoreSystemPartitionInformation: couldn't open symbolic link %wZ - %x\n",
                 NtSystemPartitionDeviceName,
                 status
                );
#endif // DBG
        return;
    }

    volumeNameString.Buffer = voumeNameStringBuffer;
    volumeNameString.Length = 0;
    //
    // Leave room at the end of the buffer for a terminating null, in case we need to add one.
    //
    volumeNameString.MaximumLength = sizeof(voumeNameStringBuffer) - sizeof(WCHAR);

    status = NtQuerySymbolicLinkObject(linkHandle,
                                       &volumeNameString,
                                       NULL
                                      );

    //
    // We don't need the handle to the symbolic link any longer.
    //

    ObCloseHandle(linkHandle, KernelMode);

    if (!NT_SUCCESS(status)) {
#if DBG
        DbgPrint("IopStoreSystemPartitionInformation: couldn't query symbolic link %wZ - %x\n",
                 NtSystemPartitionDeviceName,
                 status
                );
#endif // DBG
        return;
    }

    //
    // Make sure the volume name string is null-terminated.
    //

    volumeNameString.Buffer[volumeNameString.Length / sizeof(WCHAR)] = L'\0';

    //
    // Open HKLM\SYSTEM key.
    //

    status = IopOpenRegistryKeyEx( &systemHandle,
                                   NULL,
                                   &CmRegistryMachineSystemName,
                                   KEY_ALL_ACCESS
                                   );

    if (!NT_SUCCESS(status)) {
#if DBG
        DbgPrint("IopStoreSystemPartitionInformation: couldn't open \\REGISTRY\\MACHINE\\SYSTEM - %x\n", status);
#endif // DBG
        return;
    }

    //
    // Now open/create the setup subkey.
    //

    ASSERT( sizeof(L"Setup") <= sizeof(nameBuffer) );

    nameBuffer[0] = L'S';
    nameBuffer[1] = L'e';
    nameBuffer[2] = L't';
    nameBuffer[3] = L'u';
    nameBuffer[4] = L'p';
    nameBuffer[5] = L'\0';

    nameString.MaximumLength = sizeof(L"Setup");
    nameString.Length        = sizeof(L"Setup") - sizeof(WCHAR);
    nameString.Buffer        = nameBuffer;

    status = IopCreateRegistryKeyEx( &setupHandle,
                                     systemHandle,
                                     &nameString,
                                     KEY_ALL_ACCESS,
                                     REG_OPTION_NON_VOLATILE,
                                     NULL
                                     );

    ObCloseHandle(systemHandle, KernelMode);  // Don't need the handle to the HKLM\System key anymore.

    if (!NT_SUCCESS(status)) {
#if DBG
        DbgPrint("IopStoreSystemPartitionInformation: couldn't open Setup subkey - %x\n", status);
#endif // DBG
        return;
    }

    ASSERT( sizeof(L"SystemPartition") <= sizeof(nameBuffer) );

    nameBuffer[0]  = L'S';
    nameBuffer[1]  = L'y';
    nameBuffer[2]  = L's';
    nameBuffer[3]  = L't';
    nameBuffer[4]  = L'e';
    nameBuffer[5]  = L'm';
    nameBuffer[6]  = L'P';
    nameBuffer[7]  = L'a';
    nameBuffer[8]  = L'r';
    nameBuffer[9]  = L't';
    nameBuffer[10] = L'i';
    nameBuffer[11] = L't';
    nameBuffer[12] = L'i';
    nameBuffer[13] = L'o';
    nameBuffer[14] = L'n';
    nameBuffer[15] = L'\0';

    nameString.MaximumLength = sizeof(L"SystemPartition");
    nameString.Length        = sizeof(L"SystemPartition") - sizeof(WCHAR);



    status = NtSetValueKey(setupHandle,
                            &nameString,
                            TITLE_INDEX_VALUE,
                            REG_SZ,
                            volumeNameString.Buffer,
                            volumeNameString.Length + sizeof(WCHAR)
                           );


#if DBG
    if (!NT_SUCCESS(status)) {
        DbgPrint("IopStoreSystemPartitionInformation: couldn't write SystemPartition value - %x\n", status);
    }
#endif // DBG

    ASSERT( sizeof(L"OsLoaderPath") <= sizeof(nameBuffer) );

    nameBuffer[0]  = L'O';
    nameBuffer[1]  = L's';
    nameBuffer[2]  = L'L';
    nameBuffer[3]  = L'o';
    nameBuffer[4]  = L'a';
    nameBuffer[5]  = L'd';
    nameBuffer[6]  = L'e';
    nameBuffer[7]  = L'r';
    nameBuffer[8]  = L'P';
    nameBuffer[9]  = L'a';
    nameBuffer[10] = L't';
    nameBuffer[11] = L'h';
    nameBuffer[12] = L'\0';

    nameString.MaximumLength = sizeof(L"OsLoaderPath");
    nameString.Length        = sizeof(L"OsLoaderPath") - sizeof(WCHAR);

    //
    // Strip off the trailing backslash from the path (unless, of course, the path is a
    // single backslash).
    //

    if ((OsLoaderPathName->Length > sizeof(WCHAR)) &&
        (*(PWCHAR)((PCHAR)OsLoaderPathName->Buffer + OsLoaderPathName->Length - sizeof(WCHAR)) == L'\\')) {

        OsLoaderPathName->Length -= sizeof(WCHAR);
        *(PWCHAR)((PCHAR)OsLoaderPathName->Buffer + OsLoaderPathName->Length) = L'\0';
    }

    status = NtSetValueKey(setupHandle,
                           &nameString,
                           TITLE_INDEX_VALUE,
                           REG_SZ,
                           OsLoaderPathName->Buffer,
                           OsLoaderPathName->Length + sizeof(WCHAR)
                           );
#if DBG
    if (!NT_SUCCESS(status)) {
        DbgPrint("IopStoreSystemPartitionInformation: couldn't write OsLoaderPath value - %x\n", status);
    }
#endif // DBG

    ObCloseHandle(setupHandle, KernelMode);
}

NTSTATUS
IopLogErrorEvent(
    IN ULONG            SequenceNumber,
    IN ULONG            UniqueErrorValue,
    IN NTSTATUS         FinalStatus,
    IN NTSTATUS         SpecificIOStatus,
    IN ULONG            LengthOfInsert1,
    IN PWCHAR           Insert1,
    IN ULONG            LengthOfInsert2,
    IN PWCHAR           Insert2
    )

/*++

Routine Description:

    This routine allocates an error log entry, copies the supplied data
    to it, and requests that it be written to the error log file.

Arguments:
    SequenceNumber - A value that is unique to an IRP over the life of the irp in
    this driver. - 0 generally means an error not associated with an IRP

    UniqueErrorValue - A unique long word that identifies the particular
    call to this function.

    FinalStatus - The final status given to the irp that was associated
    with this error.  If this log entry is being made during one of
    the retries this value will be STATUS_SUCCESS.

    SpecificIOStatus - The IO status for a particular error.

    LengthOfInsert1 - The length in bytes (including the terminating NULL)
                      of the first insertion string.

    Insert1 - The first insertion string.

    LengthOfInsert2 - The length in bytes (including the terminating NULL)
                      of the second insertion string.  NOTE, there must
                      be a first insertion string for their to be
                      a second insertion string.

    Insert2 - The second insertion string.

Return Value:

    STATUS_SUCCESS - Success
    STATUS_INVALID_HANDLE - Uninitialized error log device object
    STATUS_NO_DATA_DETECTED - NULL Error log entry

--*/

{
    PIO_ERROR_LOG_PACKET errorLogEntry;
    PUCHAR ptrToFirstInsert;
    PUCHAR ptrToSecondInsert;

    if (!IopErrorLogObject) {
        return(STATUS_INVALID_HANDLE);
    }


    errorLogEntry = IoAllocateErrorLogEntry(
                        IopErrorLogObject,
                        (UCHAR)( sizeof(IO_ERROR_LOG_PACKET) +
                                LengthOfInsert1 +
                                LengthOfInsert2) );

   if ( errorLogEntry != NULL ) {

      errorLogEntry->ErrorCode = SpecificIOStatus;
      errorLogEntry->SequenceNumber = SequenceNumber;
      errorLogEntry->MajorFunctionCode = 0;
      errorLogEntry->RetryCount = 0;
      errorLogEntry->UniqueErrorValue = UniqueErrorValue;
      errorLogEntry->FinalStatus = FinalStatus;
      errorLogEntry->DumpDataSize = 0;

      ptrToFirstInsert = (PUCHAR)&errorLogEntry->DumpData[0];

      ptrToSecondInsert = ptrToFirstInsert + LengthOfInsert1;

      if (LengthOfInsert1) {

         errorLogEntry->NumberOfStrings = 1;
         errorLogEntry->StringOffset = (USHORT)(ptrToFirstInsert -
                                                (PUCHAR)errorLogEntry);
         RtlCopyMemory(
                      ptrToFirstInsert,
                      Insert1,
                      LengthOfInsert1
                      );

         if (LengthOfInsert2) {

            errorLogEntry->NumberOfStrings = 2;
            RtlCopyMemory(
                         ptrToSecondInsert,
                         Insert2,
                         LengthOfInsert2
                         );

         } //LenghtOfInsert2

      } // LenghtOfInsert1

      IoWriteErrorLogEntry(errorLogEntry);
      return(STATUS_SUCCESS);

   }  // errorLogEntry != NULL

    return(STATUS_NO_DATA_DETECTED);

} //IopLogErrorEvent

BOOLEAN
IopInitializeReserveIrp(
    PIOP_RESERVE_IRP_ALLOCATOR  Allocator
    )
/*++

Routine Description:

    This routine initializes the reserve IRP allocator for paging reads.

Arguments:

    Allocator - Pointer to the reserve IRP allocator structure.
        created by the OS Loader.

Return Value:

    The function value is a BOOLEAN indicating whether or not the reserver allocator
    was successfully initialized.

--*/
{
    Allocator->ReserveIrpStackSize = MAX_RESERVE_IRP_STACK_SIZE;
    Allocator->ReserveIrp = IoAllocateIrp(MAX_RESERVE_IRP_STACK_SIZE, FALSE);
    if (Allocator->ReserveIrp == NULL) {
        return FALSE;
    }

    Allocator->IrpAllocated = FALSE;
    KeInitializeEvent(&Allocator->Event, SynchronizationEvent, FALSE);

    return TRUE;
}

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif // ALLOC_DATA_PRAGMA

