/*
 * PROJECT:         ReactOS Kernel
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            ntoskrnl/ex/init.c
 * PURPOSE:         Executive Initialization Code
 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
 *                  Eric Kohl
 */

/* INCLUDES ******************************************************************/

#include <ntoskrnl.h>
#include <reactos/buildno.h>
#define NDEBUG
#include <debug.h>

/* This is the size that we can expect from the win 2003 loader */
#define LOADER_PARAMETER_EXTENSION_MIN_SIZE \
    RTL_SIZEOF_THROUGH_FIELD(LOADER_PARAMETER_EXTENSION, AcpiTableSize)

/* Temporary hack */
BOOLEAN
NTAPI
MmArmInitSystem(
    IN ULONG Phase,
    IN PLOADER_PARAMETER_BLOCK LoaderBlock
);

typedef struct _INIT_BUFFER
{
    WCHAR DebugBuffer[256];
    CHAR VersionBuffer[256];
    CHAR BootlogHeader[256];
    CHAR VersionNumber[24];
    RTL_USER_PROCESS_INFORMATION ProcessInfo;
    WCHAR RegistryBuffer[256];
} INIT_BUFFER, *PINIT_BUFFER;

/* DATA **********************************************************************/

/* NT Version Info */
ULONG NtMajorVersion = VER_PRODUCTMAJORVERSION;
ULONG NtMinorVersion = VER_PRODUCTMINORVERSION;
#if DBG /* Checked Build */
ULONG NtBuildNumber = VER_PRODUCTBUILD | 0xC0000000;
#else   /* Free Build */
ULONG NtBuildNumber = VER_PRODUCTBUILD;
#endif

/* NT System Info */
ULONG NtGlobalFlag = 0;
ULONG ExSuiteMask;

/* Cm Version Info */
ULONG CmNtSpBuildNumber;
ULONG CmNtCSDVersion;
ULONG CmNtCSDReleaseType;
UNICODE_STRING CmVersionString;
UNICODE_STRING CmCSDVersionString;

CHAR NtBuildLab[] = KERNEL_VERSION_BUILD_STR "."
                    REACTOS_COMPILER_NAME "_" REACTOS_COMPILER_VERSION;

/* Init flags and settings */
ULONG ExpInitializationPhase;
BOOLEAN ExpInTextModeSetup;
BOOLEAN IoRemoteBootClient;
ULONG InitSafeBootMode;
BOOLEAN InitIsWinPEMode, InitWinPEModeType;

/* NT Boot Path */
UNICODE_STRING NtSystemRoot;

/* NT Initial User Application */
WCHAR NtInitialUserProcessBuffer[128] = L"\\SystemRoot\\System32\\smss.exe";
ULONG NtInitialUserProcessBufferLength = sizeof(NtInitialUserProcessBuffer) -
                                         sizeof(WCHAR);
ULONG NtInitialUserProcessBufferType = REG_SZ;

/* Boot NLS information */
PVOID ExpNlsTableBase;
ULONG ExpAnsiCodePageDataOffset, ExpOemCodePageDataOffset;
ULONG ExpUnicodeCaseTableDataOffset;
NLSTABLEINFO ExpNlsTableInfo;
SIZE_T ExpNlsTableSize;
PVOID ExpNlsSectionPointer;

/* CMOS Timer Sanity */
BOOLEAN ExCmosClockIsSane = TRUE;
BOOLEAN ExpRealTimeIsUniversal;

/* FUNCTIONS ****************************************************************/

INIT_FUNCTION
NTSTATUS
NTAPI
ExpCreateSystemRootLink(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    UNICODE_STRING LinkName;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE LinkHandle;
    NTSTATUS Status;
    ANSI_STRING AnsiName;
    CHAR Buffer[256];
    ANSI_STRING TargetString;
    UNICODE_STRING TargetName;

    /* Initialize the ArcName tree */
    RtlInitUnicodeString(&LinkName, L"\\ArcName");
    InitializeObjectAttributes(&ObjectAttributes,
                               &LinkName,
                               OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
                               NULL,
                               SePublicDefaultUnrestrictedSd);

    /* Create it */
    Status = NtCreateDirectoryObject(&LinkHandle,
                                     DIRECTORY_ALL_ACCESS,
                                     &ObjectAttributes);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED, Status, 1, 0, 0);
    }

    /* Close the LinkHandle */
    NtClose(LinkHandle);

    /* Initialize the Device tree */
    RtlInitUnicodeString(&LinkName, L"\\Device");
    InitializeObjectAttributes(&ObjectAttributes,
                               &LinkName,
                               OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
                               NULL,
                               SePublicDefaultUnrestrictedSd);

    /* Create it */
    Status = NtCreateDirectoryObject(&LinkHandle,
                                     DIRECTORY_ALL_ACCESS,
                                     &ObjectAttributes);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED, Status, 2, 0, 0);
    }

    /* Close the LinkHandle */
    ObCloseHandle(LinkHandle, KernelMode);

    /* Create the system root symlink name */
    RtlInitAnsiString(&AnsiName, "\\SystemRoot");
    Status = RtlAnsiStringToUnicodeString(&LinkName, &AnsiName, TRUE);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED, Status, 3, 0, 0);
    }

    /* Initialize the attributes for the link */
    InitializeObjectAttributes(&ObjectAttributes,
                               &LinkName,
                               OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
                               NULL,
                               SePublicDefaultUnrestrictedSd);

    /* Build the ARC name */
    sprintf(Buffer,
            "\\ArcName\\%s%s",
            LoaderBlock->ArcBootDeviceName,
            LoaderBlock->NtBootPathName);
    Buffer[strlen(Buffer) - 1] = ANSI_NULL;

    /* Convert it to Unicode */
    RtlInitString(&TargetString, Buffer);
    Status = RtlAnsiStringToUnicodeString(&TargetName,
                                          &TargetString,
                                          TRUE);
    if (!NT_SUCCESS(Status))
    {
        /* We failed, bugcheck */
        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED, Status, 4, 0, 0);
    }

    /* Create it */
    Status = NtCreateSymbolicLinkObject(&LinkHandle,
                                        SYMBOLIC_LINK_ALL_ACCESS,
                                        &ObjectAttributes,
                                        &TargetName);

    /* Free the strings */
    RtlFreeUnicodeString(&LinkName);
    RtlFreeUnicodeString(&TargetName);

    /* Check if creating the link failed */
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED, Status, 5, 0, 0);
    }

    /* Close the handle and return success */
    ObCloseHandle(LinkHandle, KernelMode);
    return STATUS_SUCCESS;
}

INIT_FUNCTION
VOID
NTAPI
ExpInitNls(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    LARGE_INTEGER SectionSize;
    NTSTATUS Status;
    HANDLE NlsSection;
    PVOID SectionBase = NULL;
    SIZE_T ViewSize = 0;
    LARGE_INTEGER SectionOffset = {{0, 0}};
    PLIST_ENTRY ListHead, NextEntry;
    PMEMORY_ALLOCATION_DESCRIPTOR MdBlock;
    ULONG NlsTablesEncountered = 0;
    SIZE_T NlsTableSizes[3] = {0, 0, 0}; /* 3 NLS tables */

    /* Check if this is boot-time phase 0 initialization */
    if (!ExpInitializationPhase)
    {
        /* Loop the memory descriptors */
        ListHead = &LoaderBlock->MemoryDescriptorListHead;
        NextEntry = ListHead->Flink;
        while (NextEntry != ListHead)
        {
            /* Get the current block */
            MdBlock = CONTAINING_RECORD(NextEntry,
                                        MEMORY_ALLOCATION_DESCRIPTOR,
                                        ListEntry);

            /* Check if this is an NLS block */
            if (MdBlock->MemoryType == LoaderNlsData)
            {
                /* Increase the table size */
                ExpNlsTableSize += MdBlock->PageCount * PAGE_SIZE;

                /* FreeLdr-specific */
                NlsTableSizes[NlsTablesEncountered] = MdBlock->PageCount * PAGE_SIZE;
                NlsTablesEncountered++;
                ASSERT(NlsTablesEncountered < 4);
            }

            /* Go to the next block */
            NextEntry = MdBlock->ListEntry.Flink;
        }

        /* Allocate the a new buffer since loader memory will be freed */
        ExpNlsTableBase = ExAllocatePoolWithTag(NonPagedPool,
                                                ExpNlsTableSize,
                                                TAG_RTLI);
        if (!ExpNlsTableBase) KeBugCheck(PHASE0_INITIALIZATION_FAILED);

        /* Copy the codepage data in its new location. */
        if (NlsTablesEncountered == 1)
        {
            /* Ntldr-way boot process */
            RtlCopyMemory(ExpNlsTableBase,
                          LoaderBlock->NlsData->AnsiCodePageData,
                          ExpNlsTableSize);
        }
        else
        {
            /*
            * In NT, the memory blocks are contiguous, but in ReactOS they aren't,
            * so unless someone fixes FreeLdr, we'll have to use this icky hack.
            */
            RtlCopyMemory(ExpNlsTableBase,
                          LoaderBlock->NlsData->AnsiCodePageData,
                          NlsTableSizes[0]);

            RtlCopyMemory((PVOID)((ULONG_PTR)ExpNlsTableBase + NlsTableSizes[0]),
                          LoaderBlock->NlsData->OemCodePageData,
                          NlsTableSizes[1]);

            RtlCopyMemory((PVOID)((ULONG_PTR)ExpNlsTableBase + NlsTableSizes[0] +
                          NlsTableSizes[1]),
                          LoaderBlock->NlsData->UnicodeCodePageData,
                          NlsTableSizes[2]);
            /* End of Hack */
        }

        /* Initialize and reset the NLS TAbles */
        RtlInitNlsTables((PVOID)((ULONG_PTR)ExpNlsTableBase +
                                 ExpAnsiCodePageDataOffset),
                         (PVOID)((ULONG_PTR)ExpNlsTableBase +
                                 ExpOemCodePageDataOffset),
                         (PVOID)((ULONG_PTR)ExpNlsTableBase +
                                 ExpUnicodeCaseTableDataOffset),
                         &ExpNlsTableInfo);
        RtlResetRtlTranslations(&ExpNlsTableInfo);
        return;
    }

    /* Set the section size */
    SectionSize.QuadPart = ExpNlsTableSize;

    /* Create the NLS Section */
    Status = ZwCreateSection(&NlsSection,
                             SECTION_ALL_ACCESS,
                             NULL,
                             &SectionSize,
                             PAGE_READWRITE,
                             SEC_COMMIT | 0x1,
                             NULL);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 1, 0, 0);
    }

    /* Get a pointer to the section */
    Status = ObReferenceObjectByHandle(NlsSection,
                                       SECTION_ALL_ACCESS,
                                       MmSectionObjectType,
                                       KernelMode,
                                       &ExpNlsSectionPointer,
                                       NULL);
    ObCloseHandle(NlsSection, KernelMode);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 2, 0, 0);
    }

    /* Map the NLS Section in system space */
    Status = MmMapViewInSystemSpace(ExpNlsSectionPointer,
                                    &SectionBase,
                                    &ExpNlsTableSize);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 3, 0, 0);
    }

    /* Copy the codepage data in its new location. */
    ASSERT(SectionBase >= MmSystemRangeStart);
    RtlCopyMemory(SectionBase, ExpNlsTableBase, ExpNlsTableSize);

    /* Free the previously allocated buffer and set the new location */
    ExFreePoolWithTag(ExpNlsTableBase, TAG_RTLI);
    ExpNlsTableBase = SectionBase;

    /* Initialize the NLS Tables */
    RtlInitNlsTables((PVOID)((ULONG_PTR)ExpNlsTableBase +
                             ExpAnsiCodePageDataOffset),
                     (PVOID)((ULONG_PTR)ExpNlsTableBase +
                             ExpOemCodePageDataOffset),
                     (PVOID)((ULONG_PTR)ExpNlsTableBase +
                             ExpUnicodeCaseTableDataOffset),
                     &ExpNlsTableInfo);
    RtlResetRtlTranslations(&ExpNlsTableInfo);

    /* Reset the base to 0 */
    SectionBase = NULL;

    /* Map the section in the system process */
    Status = MmMapViewOfSection(ExpNlsSectionPointer,
                                PsGetCurrentProcess(),
                                &SectionBase,
                                0L,
                                0L,
                                &SectionOffset,
                                &ViewSize,
                                ViewShare,
                                0L,
                                PAGE_READWRITE);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 5, 0, 0);
    }

    /* Copy the table into the system process and set this as the base */
    RtlCopyMemory(SectionBase, ExpNlsTableBase, ExpNlsTableSize);
    ExpNlsTableBase = SectionBase;
}

INIT_FUNCTION
VOID
NTAPI
ExpLoadInitialProcess(IN PINIT_BUFFER InitBuffer,
                      OUT PRTL_USER_PROCESS_PARAMETERS *ProcessParameters,
                      OUT PCHAR *ProcessEnvironment)
{
    NTSTATUS Status;
    SIZE_T Size;
    PWSTR p;
    UNICODE_STRING NullString = RTL_CONSTANT_STRING(L"");
    UNICODE_STRING SmssName, Environment, SystemDriveString, DebugString;
    PVOID EnvironmentPtr = NULL;
    PRTL_USER_PROCESS_INFORMATION ProcessInformation;
    PRTL_USER_PROCESS_PARAMETERS ProcessParams = NULL;

    NullString.Length = sizeof(WCHAR);

    /* Use the initial buffer, after the strings */
    ProcessInformation = &InitBuffer->ProcessInfo;

    /* Allocate memory for the process parameters */
    Size = sizeof(*ProcessParams) + ((MAX_PATH * 6) * sizeof(WCHAR));
    Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
                                     (PVOID*)&ProcessParams,
                                     0,
                                     &Size,
                                     MEM_RESERVE | MEM_COMMIT,
                                     PAGE_READWRITE);
    if (!NT_SUCCESS(Status))
    {
        /* Failed, display error */
        _snwprintf(InitBuffer->DebugBuffer,
                   sizeof(InitBuffer->DebugBuffer)/sizeof(WCHAR),
                   L"INIT: Unable to allocate Process Parameters. 0x%lx",
                   Status);
        RtlInitUnicodeString(&DebugString, InitBuffer->DebugBuffer);
        ZwDisplayString(&DebugString);

        /* Bugcheck the system */
        KeBugCheckEx(SESSION1_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Setup the basic header, and give the process the low 1MB to itself */
    ProcessParams->Length = (ULONG)Size;
    ProcessParams->MaximumLength = (ULONG)Size;
    ProcessParams->Flags = RTL_USER_PROCESS_PARAMETERS_NORMALIZED |
                           RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;

    /* Allocate a page for the environment */
    Size = PAGE_SIZE;
    Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
                                     &EnvironmentPtr,
                                     0,
                                     &Size,
                                     MEM_RESERVE | MEM_COMMIT,
                                     PAGE_READWRITE);
    if (!NT_SUCCESS(Status))
    {
        /* Failed, display error */
        _snwprintf(InitBuffer->DebugBuffer,
                   sizeof(InitBuffer->DebugBuffer)/sizeof(WCHAR),
                   L"INIT: Unable to allocate Process Environment. 0x%lx",
                   Status);
        RtlInitUnicodeString(&DebugString, InitBuffer->DebugBuffer);
        ZwDisplayString(&DebugString);

        /* Bugcheck the system */
        KeBugCheckEx(SESSION2_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Write the pointer */
    ProcessParams->Environment = EnvironmentPtr;

    /* Make a buffer for the DOS path */
    p = (PWSTR)(ProcessParams + 1);
    ProcessParams->CurrentDirectory.DosPath.Buffer = p;
    ProcessParams->CurrentDirectory.DosPath.MaximumLength = MAX_PATH *
                                                            sizeof(WCHAR);

    /* Copy the DOS path */
    RtlCopyUnicodeString(&ProcessParams->CurrentDirectory.DosPath,
                         &NtSystemRoot);

    /* Make a buffer for the DLL Path */
    p = (PWSTR)((PCHAR)ProcessParams->CurrentDirectory.DosPath.Buffer +
                ProcessParams->CurrentDirectory.DosPath.MaximumLength);
    ProcessParams->DllPath.Buffer = p;
    ProcessParams->DllPath.MaximumLength = MAX_PATH * sizeof(WCHAR);

    /* Copy the DLL path and append the system32 directory */
    RtlCopyUnicodeString(&ProcessParams->DllPath,
                         &ProcessParams->CurrentDirectory.DosPath);
    RtlAppendUnicodeToString(&ProcessParams->DllPath, L"\\System32");

    /* Make a buffer for the image name */
    p = (PWSTR)((PCHAR)ProcessParams->DllPath.Buffer +
                ProcessParams->DllPath.MaximumLength);
    ProcessParams->ImagePathName.Buffer = p;
    ProcessParams->ImagePathName.MaximumLength = MAX_PATH * sizeof(WCHAR);

    /* Make sure the buffer is a valid string which within the given length */
    if ((NtInitialUserProcessBufferType != REG_SZ) ||
        ((NtInitialUserProcessBufferLength != MAXULONG) &&
         ((NtInitialUserProcessBufferLength < sizeof(WCHAR)) ||
          (NtInitialUserProcessBufferLength >
           sizeof(NtInitialUserProcessBuffer) - sizeof(WCHAR)))))
    {
        /* Invalid initial process string, bugcheck */
        KeBugCheckEx(SESSION2_INITIALIZATION_FAILED,
                     STATUS_INVALID_PARAMETER,
                     NtInitialUserProcessBufferType,
                     NtInitialUserProcessBufferLength,
                     sizeof(NtInitialUserProcessBuffer));
    }

    /* Cut out anything after a space */
    p = NtInitialUserProcessBuffer;
    while ((*p) && (*p != L' ')) p++;

    /* Set the image path length */
    ProcessParams->ImagePathName.Length =
        (USHORT)((PCHAR)p - (PCHAR)NtInitialUserProcessBuffer);

    /* Copy the actual buffer */
    RtlCopyMemory(ProcessParams->ImagePathName.Buffer,
                  NtInitialUserProcessBuffer,
                  ProcessParams->ImagePathName.Length);

    /* Null-terminate it */
    ProcessParams->ImagePathName.Buffer[ProcessParams->ImagePathName.Length /
                                        sizeof(WCHAR)] = UNICODE_NULL;

    /* Make a buffer for the command line */
    p = (PWSTR)((PCHAR)ProcessParams->ImagePathName.Buffer +
                ProcessParams->ImagePathName.MaximumLength);
    ProcessParams->CommandLine.Buffer = p;
    ProcessParams->CommandLine.MaximumLength = MAX_PATH * sizeof(WCHAR);

    /* Add the image name to the command line */
    RtlAppendUnicodeToString(&ProcessParams->CommandLine,
                             NtInitialUserProcessBuffer);

    /* Create the environment string */
    RtlInitEmptyUnicodeString(&Environment,
                              ProcessParams->Environment,
                              (USHORT)Size);

    /* Append the DLL path to it */
    RtlAppendUnicodeToString(&Environment, L"Path=");
    RtlAppendUnicodeStringToString(&Environment, &ProcessParams->DllPath);
    RtlAppendUnicodeStringToString(&Environment, &NullString);

    /* Create the system drive string */
    SystemDriveString = NtSystemRoot;
    SystemDriveString.Length = 2 * sizeof(WCHAR);

    /* Append it to the environment */
    RtlAppendUnicodeToString(&Environment, L"SystemDrive=");
    RtlAppendUnicodeStringToString(&Environment, &SystemDriveString);
    RtlAppendUnicodeStringToString(&Environment, &NullString);

    /* Append the system root to the environment */
    RtlAppendUnicodeToString(&Environment, L"SystemRoot=");
    RtlAppendUnicodeStringToString(&Environment, &NtSystemRoot);
    RtlAppendUnicodeStringToString(&Environment, &NullString);

    /* Prepare the prefetcher */
    //CcPfBeginBootPhase(150);

    /* Create SMSS process */
    SmssName = ProcessParams->ImagePathName;
    Status = RtlCreateUserProcess(&SmssName,
                                  OBJ_CASE_INSENSITIVE,
                                  RtlDeNormalizeProcessParams(ProcessParams),
                                  NULL,
                                  NULL,
                                  NULL,
                                  FALSE,
                                  NULL,
                                  NULL,
                                  ProcessInformation);
    if (!NT_SUCCESS(Status))
    {
        /* Failed, display error */
        _snwprintf(InitBuffer->DebugBuffer,
                   sizeof(InitBuffer->DebugBuffer)/sizeof(WCHAR),
                   L"INIT: Unable to create Session Manager. 0x%lx",
                   Status);
        RtlInitUnicodeString(&DebugString, InitBuffer->DebugBuffer);
        ZwDisplayString(&DebugString);

        /* Bugcheck the system */
        KeBugCheckEx(SESSION3_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Resume the thread */
    Status = ZwResumeThread(ProcessInformation->ThreadHandle, NULL);
    if (!NT_SUCCESS(Status))
    {
        /* Failed, display error */
        _snwprintf(InitBuffer->DebugBuffer,
                   sizeof(InitBuffer->DebugBuffer)/sizeof(WCHAR),
                   L"INIT: Unable to resume Session Manager. 0x%lx",
                   Status);
        RtlInitUnicodeString(&DebugString, InitBuffer->DebugBuffer);
        ZwDisplayString(&DebugString);

        /* Bugcheck the system */
        KeBugCheckEx(SESSION4_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Return success */
    *ProcessParameters = ProcessParams;
    *ProcessEnvironment = EnvironmentPtr;
}

INIT_FUNCTION
ULONG
NTAPI
ExComputeTickCountMultiplier(IN ULONG ClockIncrement)
{
    ULONG MsRemainder = 0, MsIncrement;
    ULONG IncrementRemainder;
    ULONG i;

    /* Count the number of milliseconds for each clock interrupt */
    MsIncrement = ClockIncrement / (10 * 1000);

    /* Count the remainder from the division above, with 24-bit precision */
    IncrementRemainder = ClockIncrement - (MsIncrement * (10 * 1000));
    for (i= 0; i < 24; i++)
    {
        /* Shift the remainders */
        MsRemainder <<= 1;
        IncrementRemainder <<= 1;

        /* Check if we've went past 1 ms */
        if (IncrementRemainder >= (10 * 1000))
        {
            /* Increase the remainder by one, and substract from increment */
            IncrementRemainder -= (10 * 1000);
            MsRemainder |= 1;
        }
    }

    /* Return the increment */
    return (MsIncrement << 24) | MsRemainder;
}

INIT_FUNCTION
BOOLEAN
NTAPI
ExpInitSystemPhase0(VOID)
{
    /* Initialize EXRESOURCE Support */
    ExpResourceInitialization();

    /* Initialize the environment lock */
    ExInitializeFastMutex(&ExpEnvironmentLock);

    /* Initialize the lookaside lists and locks */
    ExpInitLookasideLists();

    /* Initialize the Firmware Table resource and listhead */
    InitializeListHead(&ExpFirmwareTableProviderListHead);
    ExInitializeResourceLite(&ExpFirmwareTableResource);

    /* Set the suite mask to maximum and return */
    ExSuiteMask = 0xFFFFFFFF;
    return TRUE;
}

INIT_FUNCTION
BOOLEAN
NTAPI
ExpInitSystemPhase1(VOID)
{
    /* Initialize worker threads */
    ExpInitializeWorkerThreads();

    /* Initialize pushlocks */
    ExpInitializePushLocks();

    /* Initialize events and event pairs */
    if (ExpInitializeEventImplementation() == FALSE)
    {
        DPRINT1("Executive: Event initialization failed\n");
        return FALSE;
    }
    if (ExpInitializeEventPairImplementation() == FALSE)
    {
        DPRINT1("Executive: Event Pair initialization failed\n");
        return FALSE;
    }

    /* Initialize mutants */
    if (ExpInitializeMutantImplementation() == FALSE)
    {
        DPRINT1("Executive: Mutant initialization failed\n");
        return FALSE;
    }

    /* Initialize callbacks */
    if (ExpInitializeCallbacks() == FALSE)
    {
        DPRINT1("Executive: Callback initialization failed\n");
        return FALSE;
    }

    /* Initialize semaphores */
    if (ExpInitializeSemaphoreImplementation() == FALSE)
    {
        DPRINT1("Executive: Semaphore initialization failed\n");
        return FALSE;
    }

    /* Initialize timers */
    if (ExpInitializeTimerImplementation() == FALSE)
    {
        DPRINT1("Executive: Timer initialization failed\n");
        return FALSE;
    }

    /* Initialize profiling */
    if (ExpInitializeProfileImplementation() == FALSE)
    {
        DPRINT1("Executive: Profile initialization failed\n");
        return FALSE;
    }

    /* Initialize UUIDs */
    if (ExpUuidInitialization() == FALSE)
    {
        DPRINT1("Executive: Uuid initialization failed\n");
        return FALSE;
    }

    /* Initialize keyed events */
    if (ExpInitializeKeyedEventImplementation() == FALSE)
    {
        DPRINT1("Executive: Keyed event initialization failed\n");
        return FALSE;
    }

    /* Initialize Win32K */
    if (ExpWin32kInit() == FALSE)
    {
        DPRINT1("Executive: Win32 initialization failed\n");
        return FALSE;
    }
    return TRUE;
}

INIT_FUNCTION
BOOLEAN
NTAPI
ExInitSystem(VOID)
{
    /* Check the initialization phase */
    switch (ExpInitializationPhase)
    {
        case 0:

            /* Do Phase 0 */
            return ExpInitSystemPhase0();

        case 1:

            /* Do Phase 1 */
            return ExpInitSystemPhase1();

        default:

            /* Don't know any other phase! Bugcheck! */
            KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
            return FALSE;
    }
}

INIT_FUNCTION
BOOLEAN
NTAPI
ExpIsLoaderValid(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    PLOADER_PARAMETER_EXTENSION Extension;

    /* Get the loader extension */
    Extension = LoaderBlock->Extension;

    /* Validate the size (Windows 2003 loader doesn't provide more) */
    if (Extension->Size < LOADER_PARAMETER_EXTENSION_MIN_SIZE) return FALSE;

    /* Don't validate upper versions */
    if (Extension->MajorVersion > VER_PRODUCTMAJORVERSION) return TRUE;

    /* Fail if this is NT 4 */
    if (Extension->MajorVersion < VER_PRODUCTMAJORVERSION) return FALSE;

    /* Fail if this is XP */
    if (Extension->MinorVersion < VER_PRODUCTMINORVERSION) return FALSE;

    /* This is 2003 or newer, approve it */
    return TRUE;
}

INIT_FUNCTION
VOID
NTAPI
ExpLoadBootSymbols(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    ULONG i = 0;
    PLIST_ENTRY NextEntry;
    ULONG Count, Length;
    PWCHAR Name;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    CHAR NameBuffer[256];
    STRING SymbolString;
    NTSTATUS Status;

    /* Loop the driver list */
    NextEntry = LoaderBlock->LoadOrderListHead.Flink;
    while (NextEntry != &LoaderBlock->LoadOrderListHead)
    {
        /* Skip the first two images */
        if (i >= 2)
        {
            /* Get the entry */
            LdrEntry = CONTAINING_RECORD(NextEntry,
                                         LDR_DATA_TABLE_ENTRY,
                                         InLoadOrderLinks);
            if (LdrEntry->FullDllName.Buffer[0] == L'\\')
            {
                /* We have a name, read its data */
                Name = LdrEntry->FullDllName.Buffer;
                Length = LdrEntry->FullDllName.Length / sizeof(WCHAR);

                /* Check if our buffer can hold it */
                if (sizeof(NameBuffer) < Length + sizeof(ANSI_NULL))
                {
                    /* It's too long */
                    Status = STATUS_BUFFER_OVERFLOW;
                }
                else
                {
                    /* Copy the name */
                    Count = 0;
                    do
                    {
                        /* Copy the character */
                        NameBuffer[Count++] = (CHAR)*Name++;
                    } while (Count < Length);

                    /* Null-terminate */
                    NameBuffer[Count] = ANSI_NULL;
                    Status = STATUS_SUCCESS;
                }
            }
            else
            {
                /* Safely print the string into our buffer */
                Status = RtlStringCbPrintfA(NameBuffer,
                                            sizeof(NameBuffer),
                                            "%S\\System32\\Drivers\\%wZ",
                                            &SharedUserData->NtSystemRoot[2],
                                            &LdrEntry->BaseDllName);
            }

            /* Check if the buffer was ok */
            if (NT_SUCCESS(Status))
            {
                /* Initialize the STRING for the debugger */
                RtlInitString(&SymbolString, NameBuffer);

                /* Load the symbols */
                DbgLoadImageSymbols(&SymbolString,
                                    LdrEntry->DllBase,
                                    (ULONG_PTR)PsGetCurrentProcessId());
            }
        }

        /* Go to the next entry */
        i++;
        NextEntry = NextEntry->Flink;
    }
}

INIT_FUNCTION
VOID
NTAPI
ExBurnMemory(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
             IN ULONG_PTR PagesToDestroy,
             IN TYPE_OF_MEMORY MemoryType)
{
    PLIST_ENTRY ListEntry;
    PMEMORY_ALLOCATION_DESCRIPTOR MemDescriptor;

    DPRINT1("Burn RAM amount: %lu pages\n", PagesToDestroy);

    /* Loop the memory descriptors, beginning at the end */
    for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Blink;
         ListEntry != &LoaderBlock->MemoryDescriptorListHead;
         ListEntry = ListEntry->Blink)
    {
        /* Get the memory descriptor structure */
        MemDescriptor = CONTAINING_RECORD(ListEntry,
                                          MEMORY_ALLOCATION_DESCRIPTOR,
                                          ListEntry);

        /* Is memory free there or is it temporary? */
        if (MemDescriptor->MemoryType == LoaderFree ||
            MemDescriptor->MemoryType == LoaderFirmwareTemporary)
        {
            /* Check if the descriptor has more pages than we want */
            if (MemDescriptor->PageCount > PagesToDestroy)
            {
                /* Change block's page count, ntoskrnl doesn't care much */
                MemDescriptor->PageCount -= PagesToDestroy;
                break;
            }
            else
            {
                /* Change block type */
                MemDescriptor->MemoryType = MemoryType;
                PagesToDestroy -= MemDescriptor->PageCount;

                /* Check if we are done */
                if (PagesToDestroy == 0) break;
            }
        }
    }
}

INIT_FUNCTION
VOID
NTAPI
ExpInitializeExecutive(IN ULONG Cpu,
                       IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    PNLS_DATA_BLOCK NlsData;
    CHAR Buffer[256];
    ANSI_STRING AnsiPath;
    NTSTATUS Status;
    PCHAR CommandLine, PerfMem;
    ULONG PerfMemUsed;
    PLDR_DATA_TABLE_ENTRY NtosEntry;
    PMESSAGE_RESOURCE_ENTRY MsgEntry;
    ANSI_STRING CSDString;
    size_t Remaining = 0;
    PCHAR RcEnd = NULL;
    CHAR VersionBuffer[65];

    /* Validate Loader */
    if (!ExpIsLoaderValid(LoaderBlock))
    {
        /* Invalid loader version */
        KeBugCheckEx(MISMATCHED_HAL,
                     3,
                     LoaderBlock->Extension->Size,
                     LoaderBlock->Extension->MajorVersion,
                     LoaderBlock->Extension->MinorVersion);
    }

    /* Initialize PRCB pool lookaside pointers */
    ExInitPoolLookasidePointers();

    /* Check if this is an application CPU */
    if (Cpu)
    {
        /* Then simply initialize it with HAL */
        if (!HalInitSystem(ExpInitializationPhase, LoaderBlock))
        {
            /* Initialization failed */
            KeBugCheck(HAL_INITIALIZATION_FAILED);
        }

        /* We're done */
        return;
    }

    /* Assume no text-mode or remote boot */
    ExpInTextModeSetup = FALSE;
    IoRemoteBootClient = FALSE;

    /* Check if we have a setup loader block */
    if (LoaderBlock->SetupLdrBlock)
    {
        /* Check if this is text-mode setup */
        if (LoaderBlock->SetupLdrBlock->Flags & SETUPLDR_TEXT_MODE)
            ExpInTextModeSetup = TRUE;

        /* Check if this is network boot */
        if (LoaderBlock->SetupLdrBlock->Flags & SETUPLDR_REMOTE_BOOT)
        {
            /* Set variable */
            IoRemoteBootClient = TRUE;

            /* Make sure we're actually booting off the network */
            ASSERT(!_memicmp(LoaderBlock->ArcBootDeviceName, "net(0)", 6));
        }
    }

    /* Set phase to 0 */
    ExpInitializationPhase = 0;

    /* Get boot command line */
    CommandLine = LoaderBlock->LoadOptions;
    if (CommandLine)
    {
        /* Upcase it for comparison and check if we're in performance mode */
        _strupr(CommandLine);
        PerfMem = strstr(CommandLine, "PERFMEM");
        if (PerfMem)
        {
            /* Check if the user gave a number of bytes to use */
            PerfMem = strstr(PerfMem, "=");
            if (PerfMem)
            {
                /* Read the number of pages we'll use */
                PerfMemUsed = atol(PerfMem + 1) * (1024 * 1024 / PAGE_SIZE);
                if (PerfMem)
                {
                    /* FIXME: TODO */
                    DPRINT1("BBT performance mode not yet supported."
                            "/PERFMEM option ignored.\n");
                }
            }
        }

        /* Check if we're burning memory */
        PerfMem = strstr(CommandLine, "BURNMEMORY");
        if (PerfMem)
        {
            /* Check if the user gave a number of bytes to use */
            PerfMem = strstr(PerfMem, "=");
            if (PerfMem)
            {
                /* Read the number of pages we'll use */
                PerfMemUsed = atol(PerfMem + 1) * (1024 * 1024 / PAGE_SIZE);
                if (PerfMemUsed) ExBurnMemory(LoaderBlock, PerfMemUsed, LoaderBad);
            }
        }
    }

    /* Setup NLS Base and offsets */
    NlsData = LoaderBlock->NlsData;
    ExpNlsTableBase = NlsData->AnsiCodePageData;
    ExpAnsiCodePageDataOffset = 0;
    ExpOemCodePageDataOffset = (ULONG)((ULONG_PTR)NlsData->OemCodePageData -
                                       (ULONG_PTR)NlsData->AnsiCodePageData);
    ExpUnicodeCaseTableDataOffset = (ULONG)((ULONG_PTR)NlsData->UnicodeCodePageData -
                                            (ULONG_PTR)NlsData->AnsiCodePageData);

    /* Initialize the NLS Tables */
    RtlInitNlsTables((PVOID)((ULONG_PTR)ExpNlsTableBase +
                             ExpAnsiCodePageDataOffset),
                     (PVOID)((ULONG_PTR)ExpNlsTableBase +
                             ExpOemCodePageDataOffset),
                     (PVOID)((ULONG_PTR)ExpNlsTableBase +
                             ExpUnicodeCaseTableDataOffset),
                     &ExpNlsTableInfo);
    RtlResetRtlTranslations(&ExpNlsTableInfo);

    /* Now initialize the HAL */
    if (!HalInitSystem(ExpInitializationPhase, LoaderBlock))
    {
        /* HAL failed to initialize, bugcheck */
        KeBugCheck(HAL_INITIALIZATION_FAILED);
    }

    /* Make sure interrupts are active now */
    _enable();

    /* Clear the crypto exponent */
    SharedUserData->CryptoExponent = 0;

    /* Set global flags for the checked build */
#if DBG
    NtGlobalFlag |= FLG_ENABLE_CLOSE_EXCEPTIONS |
                    FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
#endif

    /* Setup NT System Root Path */
    sprintf(Buffer, "C:%s", LoaderBlock->NtBootPathName);

    /* Convert to ANSI_STRING and null-terminate it */
    RtlInitString(&AnsiPath, Buffer);
    Buffer[--AnsiPath.Length] = ANSI_NULL;

    /* Get the string from KUSER_SHARED_DATA's buffer */
    RtlInitEmptyUnicodeString(&NtSystemRoot,
                              SharedUserData->NtSystemRoot,
                              sizeof(SharedUserData->NtSystemRoot));

    /* Now fill it in */
    Status = RtlAnsiStringToUnicodeString(&NtSystemRoot, &AnsiPath, FALSE);
    if (!NT_SUCCESS(Status)) KeBugCheck(SESSION3_INITIALIZATION_FAILED);

    /* Setup bugcheck messages */
    KiInitializeBugCheck();

    /* Setup initial system settings */
    CmGetSystemControlValues(LoaderBlock->RegistryBase, CmControlVector);

    /* Set the Service Pack Number and add it to the CSD Version number if needed */
    CmNtSpBuildNumber = VER_PRODUCTBUILD_QFE;
    if (((CmNtCSDVersion & 0xFFFF0000) == 0) && (CmNtCSDReleaseType == 1))
    {
        CmNtCSDVersion |= (VER_PRODUCTBUILD_QFE << 16);
    }

    /* Add loaded CmNtGlobalFlag value */
    NtGlobalFlag |= CmNtGlobalFlag;

    /* Initialize the executive at phase 0 */
    if (!ExInitSystem()) KeBugCheck(PHASE0_INITIALIZATION_FAILED);

    /* Initialize the memory manager at phase 0 */
    if (!MmArmInitSystem(0, LoaderBlock)) KeBugCheck(PHASE0_INITIALIZATION_FAILED);

    /* Load boot symbols */
    ExpLoadBootSymbols(LoaderBlock);

    /* Check if we should break after symbol load */
    if (KdBreakAfterSymbolLoad) DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);

    /* Check if this loader is compatible with NT 5.2 */
    if (LoaderBlock->Extension->Size >= sizeof(LOADER_PARAMETER_EXTENSION))
    {
        /* Setup headless terminal settings */
        HeadlessInit(LoaderBlock);
    }

    /* Set system ranges */
#ifdef _M_AMD64
    SharedUserData->Reserved1 = MM_HIGHEST_USER_ADDRESS_WOW64;
    SharedUserData->Reserved3 = MM_SYSTEM_RANGE_START_WOW64;
#else
    SharedUserData->Reserved1 = (ULONG_PTR)MmHighestUserAddress;
    SharedUserData->Reserved3 = (ULONG_PTR)MmSystemRangeStart;
#endif

    /* Make a copy of the NLS Tables */
    ExpInitNls(LoaderBlock);

    /* Get the kernel's load entry */
    NtosEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,
                                  LDR_DATA_TABLE_ENTRY,
                                  InLoadOrderLinks);

    /* Check if this is a service pack */
    if (CmNtCSDVersion & 0xFFFF)
    {
        /* Get the service pack string */
        Status = RtlFindMessage(NtosEntry->DllBase,
                                11,
                                0,
                                WINDOWS_NT_CSD_STRING,
                                &MsgEntry);
        if (NT_SUCCESS(Status))
        {
            /* Setup the string */
            RtlInitAnsiString(&CSDString, (PCHAR)MsgEntry->Text);

            /* Remove trailing newline */
            while ((CSDString.Length > 0) &&
                   ((CSDString.Buffer[CSDString.Length - 1] == '\r') ||
                    (CSDString.Buffer[CSDString.Length - 1] == '\n')))
            {
                /* Skip the trailing character */
                CSDString.Length--;
            }

            /* Fill the buffer with version information */
            Status = RtlStringCbPrintfA(Buffer,
                                        sizeof(Buffer),
                                        "%Z %u%c",
                                        &CSDString,
                                        (CmNtCSDVersion & 0xFF00) >> 8,
                                        (CmNtCSDVersion & 0xFF) ?
                                        'A' + (CmNtCSDVersion & 0xFF) - 1 :
                                        ANSI_NULL);
        }
        else
        {
            /* Build default string */
            Status = RtlStringCbPrintfA(Buffer,
                                        sizeof(Buffer),
                                        "CSD %04x",
                                        CmNtCSDVersion);
        }

        /* Check for success */
        if (!NT_SUCCESS(Status))
        {
            /* Fail */
            KeBugCheckEx(PHASE0_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }
    }
    else
    {
        /* Then this is a beta */
        Status = RtlStringCbCopyExA(Buffer,
                                    sizeof(Buffer),
                                    VER_PRODUCTBETA_STR,
                                    NULL,
                                    &Remaining,
                                    0);
        if (!NT_SUCCESS(Status))
        {
            /* Fail */
            KeBugCheckEx(PHASE0_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }

        /* Update length */
        CmCSDVersionString.MaximumLength = sizeof(Buffer) - (USHORT)Remaining;
    }

    /* Check if we have an RC number */
    if ((CmNtCSDVersion & 0xFFFF0000) && (CmNtCSDReleaseType == 1))
    {
        /* Check if we have no version data yet */
        if (!(*Buffer))
        {
            /* Set defaults */
            Remaining = sizeof(Buffer);
            RcEnd = Buffer;
        }
        else
        {
            /* Add comma and space */
            Status = RtlStringCbCatExA(Buffer,
                                       sizeof(Buffer),
                                       ", ",
                                       &RcEnd,
                                       &Remaining,
                                       0);
            if (!NT_SUCCESS(Status))
            {
                /* Fail */
                KeBugCheckEx(PHASE0_INITIALIZATION_FAILED, Status, 0, 0, 0);
            }
        }

        /* Add the version format string */
        Status = RtlStringCbPrintfA(RcEnd,
                                    Remaining,
                                    "v.%u",
                                    (CmNtCSDVersion & 0xFFFF0000) >> 16);
        if (!NT_SUCCESS(Status))
        {
            /* Fail */
            KeBugCheckEx(PHASE0_INITIALIZATION_FAILED, Status, 0, 0, 0);
        }
    }

    /* Now setup the final string */
    RtlInitAnsiString(&CSDString, Buffer);
    Status = RtlAnsiStringToUnicodeString(&CmCSDVersionString,
                                          &CSDString,
                                          TRUE);
    if (!NT_SUCCESS(Status))
    {
        /* Fail */
        KeBugCheckEx(PHASE0_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Add our version */
    Status = RtlStringCbPrintfA(VersionBuffer,
                                sizeof(VersionBuffer),
                                "%u.%u",
                                VER_PRODUCTMAJORVERSION,
                                VER_PRODUCTMINORVERSION);
    if (!NT_SUCCESS(Status))
    {
        /* Fail */
        KeBugCheckEx(PHASE0_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Build the final version string */
    RtlCreateUnicodeStringFromAsciiz(&CmVersionString, VersionBuffer);

    /* Check if the user wants a kernel stack trace database */
    if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB)
    {
        /* FIXME: TODO */
        DPRINT1("Kernel-mode stack trace support not yet present."
                "FLG_KERNEL_STACK_TRACE_DB flag ignored.\n");
    }

    /* Check if he wanted exception logging */
    if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING)
    {
        /* FIXME: TODO */
        DPRINT1("Kernel-mode exception logging support not yet present."
                "FLG_ENABLE_EXCEPTION_LOGGING flag ignored.\n");
    }

    /* Initialize the Handle Table */
    ExpInitializeHandleTables();

#if DBG
    /* On checked builds, allocate the system call count table */
    KeServiceDescriptorTable[0].Count =
        ExAllocatePoolWithTag(NonPagedPool,
                              KiServiceLimit * sizeof(ULONG),
                              'llaC');

    /* Use it for the shadow table too */
    KeServiceDescriptorTableShadow[0].Count = KeServiceDescriptorTable[0].Count;

    /* Make sure allocation succeeded */
    if (KeServiceDescriptorTable[0].Count)
    {
        /* Zero the call counts to 0 */
        RtlZeroMemory(KeServiceDescriptorTable[0].Count,
                      KiServiceLimit * sizeof(ULONG));
    }
#endif

    /* Create the Basic Object Manager Types to allow new Object Types */
    if (!ObInitSystem()) KeBugCheck(OBJECT_INITIALIZATION_FAILED);

    /* Load basic Security for other Managers */
    if (!SeInitSystem()) KeBugCheck(SECURITY_INITIALIZATION_FAILED);

    /* Initialize the Process Manager */
    if (!PsInitSystem(LoaderBlock)) KeBugCheck(PROCESS_INITIALIZATION_FAILED);

    /* Initialize the PnP Manager */
    if (!PpInitSystem()) KeBugCheck(PP0_INITIALIZATION_FAILED);

    /* Initialize the User-Mode Debugging Subsystem */
    DbgkInitialize();

    /* Calculate the tick count multiplier */
    ExpTickCountMultiplier = ExComputeTickCountMultiplier(KeMaximumIncrement);
    SharedUserData->TickCountMultiplier = ExpTickCountMultiplier;

    /* Set the OS Version */
    SharedUserData->NtMajorVersion = NtMajorVersion;
    SharedUserData->NtMinorVersion = NtMinorVersion;

    /* Set the machine type */
    SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_NATIVE;
    SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_NATIVE;
}

VOID
NTAPI
MmFreeLoaderBlock(IN PLOADER_PARAMETER_BLOCK LoaderBlock);

INIT_FUNCTION
VOID
NTAPI
Phase1InitializationDiscard(IN PVOID Context)
{
    PLOADER_PARAMETER_BLOCK LoaderBlock = Context;
    NTSTATUS Status, MsgStatus;
    TIME_FIELDS TimeFields;
    LARGE_INTEGER SystemBootTime, UniversalBootTime, OldTime, Timeout;
    BOOLEAN SosEnabled, NoGuiBoot, ResetBias = FALSE, AlternateShell = FALSE;
    PLDR_DATA_TABLE_ENTRY NtosEntry;
    PMESSAGE_RESOURCE_ENTRY MsgEntry;
    PCHAR CommandLine, Y2KHackRequired, SafeBoot, Environment;
    PCHAR StringBuffer, EndBuffer, BeginBuffer, MpString = "";
    PINIT_BUFFER InitBuffer;
    ANSI_STRING TempString;
    ULONG LastTzBias, Length, YearHack = 0, Disposition, MessageCode = 0;
    SIZE_T Size;
    size_t Remaining;
    PRTL_USER_PROCESS_INFORMATION ProcessInfo;
    KEY_VALUE_PARTIAL_INFORMATION KeyPartialInfo;
    UNICODE_STRING KeyName;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE KeyHandle, OptionHandle;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;

    /* Allocate the initialization buffer */
    InitBuffer = ExAllocatePoolWithTag(NonPagedPool,
                                       sizeof(INIT_BUFFER),
                                       TAG_INIT);
    if (!InitBuffer)
    {
        /* Bugcheck */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, STATUS_NO_MEMORY, 8, 0, 0);
    }

    /* Set to phase 1 */
    ExpInitializationPhase = 1;

    /* Set us at maximum priority */
    KeSetPriorityThread(KeGetCurrentThread(), HIGH_PRIORITY);

    /* Do Phase 1 HAL Initialization */
    if (!HalInitSystem(1, LoaderBlock)) KeBugCheck(HAL1_INITIALIZATION_FAILED);

    /* Get the command line and upcase it */
    CommandLine = (LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL);

    /* Check if GUI Boot is enabled */
    NoGuiBoot = (CommandLine && strstr(CommandLine, "NOGUIBOOT") != NULL);

    /* Get the SOS setting */
    SosEnabled = (CommandLine && strstr(CommandLine, "SOS") != NULL);

    /* Setup the boot driver */
    InbvEnableBootDriver(!NoGuiBoot);
    InbvDriverInitialize(LoaderBlock, IDB_MAX_RESOURCE);

    /* Check if GUI boot is enabled */
    if (!NoGuiBoot)
    {
        /* It is, display the boot logo and enable printing strings */
        InbvEnableDisplayString(SosEnabled);
        DisplayBootBitmap(SosEnabled);
    }
    else
    {
        /* Release display ownership if not using GUI boot */
        InbvNotifyDisplayOwnershipLost(NULL);

        /* Don't allow boot-time strings */
        InbvEnableDisplayString(FALSE);
    }

    /* Check if this is LiveCD (WinPE) mode */
    if (CommandLine && strstr(CommandLine, "MININT") != NULL)
    {
        /* Setup WinPE Settings */
        InitIsWinPEMode = TRUE;
        InitWinPEModeType |= (strstr(CommandLine, "INRAM") != NULL) ? 0x80000000 : 0x00000001;
    }

    /* Get the kernel's load entry */
    NtosEntry = CONTAINING_RECORD(LoaderBlock->LoadOrderListHead.Flink,
                                  LDR_DATA_TABLE_ENTRY,
                                  InLoadOrderLinks);

    /* Find the banner message */
    MsgStatus = RtlFindMessage(NtosEntry->DllBase,
                               11,
                               0,
                               WINDOWS_NT_BANNER,
                               &MsgEntry);

    /* Setup defaults and check if we have a version string */
    StringBuffer = InitBuffer->VersionBuffer;
    BeginBuffer = StringBuffer;
    EndBuffer = StringBuffer;
    Remaining = sizeof(InitBuffer->VersionBuffer);
    if (CmCSDVersionString.Length)
    {
        /* Print the version string */
        Status = RtlStringCbPrintfExA(StringBuffer,
                                      Remaining,
                                      &EndBuffer,
                                      &Remaining,
                                      0,
                                      ": %wZ",
                                      &CmCSDVersionString);
        if (!NT_SUCCESS(Status))
        {
            /* Bugcheck */
            KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 7, 0, 0);
        }
    }
    else
    {
        /* No version */
        *EndBuffer = ANSI_NULL; /* Null-terminate the string */
    }

    /* Skip over the null-terminator to start a new string */
    ++EndBuffer;
    --Remaining;

    /* Build the version number */
    StringBuffer = InitBuffer->VersionNumber;
    Status = RtlStringCbPrintfA(StringBuffer,
                                sizeof(InitBuffer->VersionNumber),
                                "%u.%u",
                                VER_PRODUCTMAJORVERSION,
                                VER_PRODUCTMINORVERSION);
    if (!NT_SUCCESS(Status))
    {
        /* Bugcheck */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 7, 0, 0);
    }

    /* Check if we had found a banner message */
    if (NT_SUCCESS(MsgStatus))
    {
        /* Create the banner message */
        /* ReactOS specific: Report ReactOS version, NtBuildLab information and reported NT kernel version */
        Status = RtlStringCbPrintfA(EndBuffer,
                                    Remaining,
                                    (PCHAR)MsgEntry->Text,
                                    KERNEL_VERSION_STR,
                                    NtBuildLab,
                                    StringBuffer,
                                    NtBuildNumber & 0xFFFF,
                                    BeginBuffer);
        if (!NT_SUCCESS(Status))
        {
            /* Bugcheck */
            KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 7, 0, 0);
        }
    }
    else
    {
        /* Use hard-coded banner message */
        Status = RtlStringCbCopyA(EndBuffer, Remaining, "REACTOS (R)\r\n");
        if (!NT_SUCCESS(Status))
        {
            /* Bugcheck */
            KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 7, 0, 0);
        }
    }

    /* Display the version string on-screen */
    InbvDisplayString(EndBuffer);

    /* Initialize Power Subsystem in Phase 0 */
    if (!PoInitSystem(0)) KeBugCheck(INTERNAL_POWER_ERROR);

    /* Check for Y2K hack */
    Y2KHackRequired = CommandLine ? strstr(CommandLine, "YEAR") : NULL;
    if (Y2KHackRequired) Y2KHackRequired = strstr(Y2KHackRequired, "=");
    if (Y2KHackRequired) YearHack = atol(Y2KHackRequired + 1);

    /* Query the clock */
    if ((ExCmosClockIsSane) && (HalQueryRealTimeClock(&TimeFields)))
    {
        /* Check if we're using the Y2K hack */
        if (Y2KHackRequired) TimeFields.Year = (CSHORT)YearHack;

        /* Convert to time fields */
        RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
        UniversalBootTime = SystemBootTime;

        /* Check if real time is GMT */
        if (!ExpRealTimeIsUniversal)
        {
            /* Check if we don't have a valid bias */
            if (ExpLastTimeZoneBias == MAXULONG)
            {
                /* Reset */
                ResetBias = TRUE;
                ExpLastTimeZoneBias = ExpAltTimeZoneBias;
            }

            /* Calculate the bias in seconds */
            ExpTimeZoneBias.QuadPart = Int32x32To64(ExpLastTimeZoneBias * 60,
                                                    10000000);

            /* Set the boot time-zone bias */
            SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.HighPart;
            SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.LowPart;
            SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.HighPart;

            /* Convert the boot time to local time, and set it */
            UniversalBootTime.QuadPart = SystemBootTime.QuadPart +
                                         ExpTimeZoneBias.QuadPart;
        }

        /* Update the system time */
        KeSetSystemTime(&UniversalBootTime, &OldTime, FALSE, NULL);

        /* Do system callback */
        PoNotifySystemTimeSet();

        /* Remember this as the boot time */
        KeBootTime = UniversalBootTime;
        KeBootTimeBias = 0;
    }

    /* Initialize all processors */
    if (!HalAllProcessorsStarted()) KeBugCheck(HAL1_INITIALIZATION_FAILED);

#ifdef CONFIG_SMP
    /* HACK: We should use RtlFindMessage and not only fallback to this */
    MpString = "MultiProcessor Kernel\r\n";
#endif

    /* Setup the "MP" String */
    RtlInitAnsiString(&TempString, MpString);

    /* Make sure to remove the \r\n if we actually have a string */
    while ((TempString.Length > 0) &&
           ((TempString.Buffer[TempString.Length - 1] == '\r') ||
            (TempString.Buffer[TempString.Length - 1] == '\n')))
    {
        /* Skip the trailing character */
        TempString.Length--;
    }

    /* Get the information string from our resource file */
    MsgStatus = RtlFindMessage(NtosEntry->DllBase,
                               11,
                               0,
                               KeNumberProcessors > 1 ?
                               WINDOWS_NT_INFO_STRING_PLURAL :
                               WINDOWS_NT_INFO_STRING,
                               &MsgEntry);

    /* Get total RAM size, in MiB */
    /* Round size up. Assumed to better match actual physical RAM size */
    Size = ALIGN_UP_BY(MmNumberOfPhysicalPages * PAGE_SIZE, 1024 * 1024) / (1024 * 1024);

    /* Create the string */
    StringBuffer = InitBuffer->VersionBuffer;
    Status = RtlStringCbPrintfA(StringBuffer,
                                sizeof(InitBuffer->VersionBuffer),
                                NT_SUCCESS(MsgStatus) ?
                                (PCHAR)MsgEntry->Text :
                                "%u System Processor [%Iu MB Memory] %Z\r\n",
                                KeNumberProcessors,
                                Size,
                                &TempString);
    if (!NT_SUCCESS(Status))
    {
        /* Bugcheck */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 4, 0, 0);
    }

    /* Display RAM and CPU count */
    InbvDisplayString(StringBuffer);

    /* Update the progress bar */
    InbvUpdateProgressBar(5);

    /* Call OB initialization again */
    if (!ObInitSystem()) KeBugCheck(OBJECT1_INITIALIZATION_FAILED);

    /* Initialize Basic System Objects and Worker Threads */
    if (!ExInitSystem()) KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, 0, 0, 1, 0);

    /* Initialize the later stages of the kernel */
    if (!KeInitSystem()) KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, 0, 0, 2, 0);

    /* Call KD Providers at Phase 1 */
    if (!KdInitSystem(ExpInitializationPhase, KeLoaderBlock))
    {
        /* Failed, bugcheck */
        KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, 0, 0, 3, 0);
    }

    /* Initialize the SRM in Phase 1 */
    if (!SeInitSystem()) KeBugCheck(SECURITY1_INITIALIZATION_FAILED);

    /* Update the progress bar */
    InbvUpdateProgressBar(10);

    /* Create SystemRoot Link */
    Status = ExpCreateSystemRootLink(LoaderBlock);
    if (!NT_SUCCESS(Status))
    {
        /* Failed to create the system root link */
        KeBugCheckEx(SYMBOLIC_INITIALIZATION_FAILED, Status, 0, 0, 0);
    }

    /* Set up Region Maps, Sections and the Paging File */
    if (!MmInitSystem(1, LoaderBlock)) KeBugCheck(MEMORY1_INITIALIZATION_FAILED);

    /* Create NLS section */
    ExpInitNls(LoaderBlock);

    /* Initialize Cache Views */
    if (!CcInitializeCacheManager()) KeBugCheck(CACHE_INITIALIZATION_FAILED);

    /* Initialize the Registry */
    if (!CmInitSystem1()) KeBugCheck(CONFIG_INITIALIZATION_FAILED);

    /* Initialize Prefetcher */
    CcPfInitializePrefetcher();

    /* Update progress bar */
    InbvUpdateProgressBar(15);

    /* Update timezone information */
    LastTzBias = ExpLastTimeZoneBias;
    ExRefreshTimeZoneInformation(&SystemBootTime);

    /* Check if we're resetting timezone data */
    if (ResetBias)
    {
        /* Convert the local time to system time */
        ExLocalTimeToSystemTime(&SystemBootTime, &UniversalBootTime);
        KeBootTime = UniversalBootTime;
        KeBootTimeBias = 0;

        /* Set the new time */
        KeSetSystemTime(&UniversalBootTime, &OldTime, FALSE, NULL);
    }
    else
    {
        /* Check if the timezone switched and update the time */
        if (LastTzBias != ExpLastTimeZoneBias) ZwSetSystemTime(NULL, NULL);
    }

    /* Initialize the File System Runtime Library */
    if (!FsRtlInitSystem()) KeBugCheck(FILE_INITIALIZATION_FAILED);

    /* Initialize range lists */
    RtlInitializeRangeListPackage();

    /* Report all resources used by HAL */
    HalReportResourceUsage();

    /* Call the debugger DLL */
    KdDebuggerInitialize1(LoaderBlock);

    /* Setup PnP Manager in phase 1 */
    if (!PpInitSystem()) KeBugCheck(PP1_INITIALIZATION_FAILED);

    /* Update progress bar */
    InbvUpdateProgressBar(20);

    /* Initialize LPC */
    if (!LpcInitSystem()) KeBugCheck(LPC_INITIALIZATION_FAILED);

    /* Make sure we have a command line */
    if (CommandLine)
    {
        /* Check if this is a safe mode boot */
        SafeBoot = strstr(CommandLine, "SAFEBOOT:");
        if (SafeBoot)
        {
            /* Check what kind of boot this is */
            SafeBoot += 9;
            if (!strncmp(SafeBoot, "MINIMAL", 7))
            {
                /* Minimal mode */
                InitSafeBootMode = 1;
                SafeBoot += 7;
                MessageCode = BOOTING_IN_SAFEMODE_MINIMAL;
            }
            else if (!strncmp(SafeBoot, "NETWORK", 7))
            {
                /* With Networking */
                InitSafeBootMode = 2;
                SafeBoot += 7;
                MessageCode = BOOTING_IN_SAFEMODE_NETWORK;
            }
            else if (!strncmp(SafeBoot, "DSREPAIR", 8))
            {
                /* Domain Server Repair */
                InitSafeBootMode = 3;
                SafeBoot += 8;
                MessageCode = BOOTING_IN_SAFEMODE_DSREPAIR;

            }
            else
            {
                /* Invalid */
                InitSafeBootMode = 0;
            }

            /* Check if there's any settings left */
            if (*SafeBoot)
            {
                /* Check if an alternate shell was requested */
                if (!strncmp(SafeBoot, "(ALTERNATESHELL)", 16))
                {
                    /* Remember this for later */
                    AlternateShell = TRUE;
                }
            }

            /* Find the message to print out */
            Status = RtlFindMessage(NtosEntry->DllBase,
                                    11,
                                    0,
                                    MessageCode,
                                    &MsgEntry);
            if (NT_SUCCESS(Status))
            {
                /* Display it */
                InbvDisplayString((PCHAR)MsgEntry->Text);
            }
        }
    }

    /* Make sure we have a command line */
    if (CommandLine)
    {
        /* Check if bootlogging is enabled */
        if (strstr(CommandLine, "BOOTLOG"))
        {
            /* Find the message to print out */
            Status = RtlFindMessage(NtosEntry->DllBase,
                                    11,
                                    0,
                                    BOOTLOG_ENABLED,
                                    &MsgEntry);
            if (NT_SUCCESS(Status))
            {
                /* Display it */
                InbvDisplayString((PCHAR)MsgEntry->Text);
            }

            /* Setup boot logging */
            //IopInitializeBootLogging(LoaderBlock, InitBuffer->BootlogHeader);
        }
    }

    /* Setup the Executive in Phase 2 */
    //ExInitSystemPhase2();

    /* Update progress bar */
    InbvUpdateProgressBar(25);

    /* No KD Time Slip is pending */
    KdpTimeSlipPending = 0;

    /* Initialize in-place execution support */
    XIPInit(LoaderBlock);

    /* Set maximum update to 75% */
    InbvSetProgressBarSubset(25, 75);

    /* Initialize the I/O Subsystem */
    if (!IoInitSystem(LoaderBlock)) KeBugCheck(IO1_INITIALIZATION_FAILED);

    /* Set maximum update to 100% */
    InbvSetProgressBarSubset(0, 100);

    /* Are we in safe mode? */
    if (InitSafeBootMode)
    {
        /* Open the safe boot key */
        RtlInitUnicodeString(&KeyName,
                             L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET"
                             L"\\CONTROL\\SAFEBOOT");
        InitializeObjectAttributes(&ObjectAttributes,
                                   &KeyName,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);
        Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
        if (NT_SUCCESS(Status))
        {
            /* First check if we have an alternate shell */
            if (AlternateShell)
            {
                /* Make sure that the registry has one setup */
                RtlInitUnicodeString(&KeyName, L"AlternateShell");
                Status = NtQueryValueKey(KeyHandle,
                                         &KeyName,
                                         KeyValuePartialInformation,
                                         &KeyPartialInfo,
                                         sizeof(KeyPartialInfo),
                                         &Length);
                if (!(NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW))
                {
                    AlternateShell = FALSE;
                }
            }

            /* Create the option key */
            RtlInitUnicodeString(&KeyName, L"Option");
            InitializeObjectAttributes(&ObjectAttributes,
                                       &KeyName,
                                       OBJ_CASE_INSENSITIVE,
                                       KeyHandle,
                                       NULL);
            Status = ZwCreateKey(&OptionHandle,
                                 KEY_ALL_ACCESS,
                                 &ObjectAttributes,
                                 0,
                                 NULL,
                                 REG_OPTION_VOLATILE,
                                 &Disposition);
            NtClose(KeyHandle);

            /* Check if the key create worked */
            if (NT_SUCCESS(Status))
            {
                /* Write the safe boot type */
                RtlInitUnicodeString(&KeyName, L"OptionValue");
                NtSetValueKey(OptionHandle,
                              &KeyName,
                              0,
                              REG_DWORD,
                              &InitSafeBootMode,
                              sizeof(InitSafeBootMode));

                /* Check if we have to use an alternate shell */
                if (AlternateShell)
                {
                    /* Remember this for later */
                    Disposition = TRUE;
                    RtlInitUnicodeString(&KeyName, L"UseAlternateShell");
                    NtSetValueKey(OptionHandle,
                                  &KeyName,
                                  0,
                                  REG_DWORD,
                                  &Disposition,
                                  sizeof(Disposition));
                }

                /* Close the options key handle */
                NtClose(OptionHandle);
            }
        }
    }

    /* Are we in Win PE mode? */
    if (InitIsWinPEMode)
    {
        /* Open the safe control key */
        RtlInitUnicodeString(&KeyName,
                             L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET"
                             L"\\CONTROL");
        InitializeObjectAttributes(&ObjectAttributes,
                                   &KeyName,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);
        Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
        if (!NT_SUCCESS(Status))
        {
            /* Bugcheck */
            KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 6, 0, 0);
        }

        /* Create the MiniNT key */
        RtlInitUnicodeString(&KeyName, L"MiniNT");
        InitializeObjectAttributes(&ObjectAttributes,
                                   &KeyName,
                                   OBJ_CASE_INSENSITIVE,
                                   KeyHandle,
                                   NULL);
        Status = ZwCreateKey(&OptionHandle,
                             KEY_ALL_ACCESS,
                             &ObjectAttributes,
                             0,
                             NULL,
                             REG_OPTION_VOLATILE,
                             &Disposition);
        if (!NT_SUCCESS(Status))
        {
            /* Bugcheck */
            KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 6, 0, 0);
        }

        /* Close the handles */
        NtClose(KeyHandle);
        NtClose(OptionHandle);
    }

    /* FIXME: This doesn't do anything for now */
    MmArmInitSystem(2, LoaderBlock);

    /* Update progress bar */
    InbvUpdateProgressBar(80);

    /* Initialize VDM support */
#if defined(_M_IX86)
    KeI386VdmInitialize();
#endif

    /* Initialize Power Subsystem in Phase 1*/
    if (!PoInitSystem(1)) KeBugCheck(INTERNAL_POWER_ERROR);

    /* Update progress bar */
    InbvUpdateProgressBar(90);

    /* Initialize the Process Manager at Phase 1 */
    if (!PsInitSystem(LoaderBlock)) KeBugCheck(PROCESS1_INITIALIZATION_FAILED);

    /* Make sure nobody touches the loader block again */
    if (LoaderBlock == KeLoaderBlock) KeLoaderBlock = NULL;
    MmFreeLoaderBlock(LoaderBlock);
    LoaderBlock = Context = NULL;

    /* Initialize the SRM in phase 1 */
    if (!SeRmInitPhase1()) KeBugCheck(PROCESS1_INITIALIZATION_FAILED);

    /* Update progress bar */
    InbvUpdateProgressBar(100);

    /* Clear the screen */
    if (InbvBootDriverInstalled) FinalizeBootLogo();

    /* Allow strings to be displayed */
    InbvEnableDisplayString(TRUE);

    /* Launch initial process */
    DPRINT("Free non-cache pages: %lx\n", MmAvailablePages + MiMemoryConsumers[MC_CACHE].PagesUsed);
    ProcessInfo = &InitBuffer->ProcessInfo;
    ExpLoadInitialProcess(InitBuffer, &ProcessParameters, &Environment);

    /* Wait 5 seconds for initial process to initialize */
    Timeout.QuadPart = Int32x32To64(5, -10000000);
    Status = ZwWaitForSingleObject(ProcessInfo->ProcessHandle, FALSE, &Timeout);
    if (Status == STATUS_SUCCESS)
    {
        /* Failed, display error */
        DPRINT1("INIT: Session Manager terminated.\n");

        /* Bugcheck the system if SMSS couldn't initialize */
        KeBugCheck(SESSION5_INITIALIZATION_FAILED);
    }

    /* Close process handles */
    ZwClose(ProcessInfo->ThreadHandle);
    ZwClose(ProcessInfo->ProcessHandle);

    /* Free the initial process environment */
    Size = 0;
    ZwFreeVirtualMemory(NtCurrentProcess(),
                        (PVOID*)&Environment,
                        &Size,
                        MEM_RELEASE);

    /* Free the initial process parameters */
    Size = 0;
    ZwFreeVirtualMemory(NtCurrentProcess(),
                        (PVOID*)&ProcessParameters,
                        &Size,
                        MEM_RELEASE);

    /* Increase init phase */
    ExpInitializationPhase++;

    /* Free the boot buffer */
    ExFreePoolWithTag(InitBuffer, TAG_INIT);
    DPRINT("Free non-cache pages: %lx\n", MmAvailablePages + MiMemoryConsumers[MC_CACHE].PagesUsed);
}

VOID
NTAPI
Phase1Initialization(IN PVOID Context)
{
    /* Do the .INIT part of Phase 1 which we can free later */
    Phase1InitializationDiscard(Context);

    /* Jump into zero page thread */
    MmZeroPageThread();
}