/*
 * PROJECT:         ReactOS Kernel
 * LICENSE:         BSD - See COPYING.ARM in the top level directory
 * FILE:            ntoskrnl/mm/ARM3/sysldr.c
 * PURPOSE:         Contains the Kernel Loader (SYSLDR) for loading PE files.
 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
 *                  ReactOS Portable Systems Group
 */

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

#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>

#define MODULE_INVOLVED_IN_ARM3
#include <mm/ARM3/miarm.h>

static
inline
VOID
sprintf_nt(IN PCHAR Buffer,
           IN PCHAR Format,
           IN ...)
{
    va_list ap;
    va_start(ap, Format);
    vsprintf(Buffer, Format, ap);
    va_end(ap);
}

/* GLOBALS ********************************************************************/

LIST_ENTRY PsLoadedModuleList;
LIST_ENTRY MmLoadedUserImageList;
KSPIN_LOCK PsLoadedModuleSpinLock;
ERESOURCE PsLoadedModuleResource;
ULONG_PTR PsNtosImageBase;
KMUTANT MmSystemLoadLock;

PFN_NUMBER MmTotalSystemDriverPages;

PVOID MmUnloadedDrivers;
PVOID MmLastUnloadedDrivers;

BOOLEAN MmMakeLowMemory;
BOOLEAN MmEnforceWriteProtection = TRUE;

PMMPTE MiKernelResourceStartPte, MiKernelResourceEndPte;
ULONG_PTR ExPoolCodeStart, ExPoolCodeEnd, MmPoolCodeStart, MmPoolCodeEnd;
ULONG_PTR MmPteCodeStart, MmPteCodeEnd;

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

PVOID
NTAPI
MiCacheImageSymbols(IN PVOID BaseAddress)
{
    ULONG DebugSize;
    PVOID DebugDirectory = NULL;
    PAGED_CODE();

    /* Make sure it's safe to access the image */
    _SEH2_TRY
    {
        /* Get the debug directory */
        DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
                                                      TRUE,
                                                      IMAGE_DIRECTORY_ENTRY_DEBUG,
                                                      &DebugSize);
    }
    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
    {
        /* Nothing */
    }
    _SEH2_END;

    /* Return the directory */
    return DebugDirectory;
}

NTSTATUS
NTAPI
MiLoadImageSection(IN OUT PVOID *SectionPtr,
                   OUT PVOID *ImageBase,
                   IN PUNICODE_STRING FileName,
                   IN BOOLEAN SessionLoad,
                   IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
    PROS_SECTION_OBJECT Section = *SectionPtr;
    NTSTATUS Status;
    PEPROCESS Process;
    PVOID Base = NULL;
    SIZE_T ViewSize = 0;
    KAPC_STATE ApcState;
    LARGE_INTEGER SectionOffset = {{0, 0}};
    BOOLEAN LoadSymbols = FALSE;
    PFN_COUNT PteCount;
    PMMPTE PointerPte, LastPte;
    PVOID DriverBase;
    MMPTE TempPte;
    KIRQL OldIrql;
    PFN_NUMBER PageFrameIndex;
    PAGED_CODE();

    /* Detect session load */
    if (SessionLoad)
    {
        /* Fail */
        UNIMPLEMENTED_DBGBREAK("Session loading not yet supported!\n");
        return STATUS_NOT_IMPLEMENTED;
    }

    /* Not session load, shouldn't have an entry */
    ASSERT(LdrEntry == NULL);

    /* Attach to the system process */
    KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);

    /* Check if we need to load symbols */
    if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
    {
        /* Yes we do */
        LoadSymbols = TRUE;
        NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
    }

    /* Map the driver */
    Process = PsGetCurrentProcess();
    Status = MmMapViewOfSection(Section,
                                Process,
                                &Base,
                                0,
                                0,
                                &SectionOffset,
                                &ViewSize,
                                ViewUnmap,
                                0,
                                PAGE_EXECUTE);

    /* Re-enable the flag */
    if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;

    /* Check if we failed with distinguished status code */
    if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
    {
        /* Change it to something more generic */
        Status = STATUS_INVALID_IMAGE_FORMAT;
    }

    /* Now check if we failed */
    if (!NT_SUCCESS(Status))
    {
        /* Detach and return */
        DPRINT1("MmMapViewOfSection failed with status 0x%x\n", Status);
        KeUnstackDetachProcess(&ApcState);
        return Status;
    }

    /* Reserve system PTEs needed */
    PteCount = ROUND_TO_PAGES(Section->ImageSection->ImageInformation.ImageFileSize) >> PAGE_SHIFT;
    PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
    if (!PointerPte)
    {
        DPRINT1("MiReserveSystemPtes failed\n");
        KeUnstackDetachProcess(&ApcState);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    /* New driver base */
    LastPte = PointerPte + PteCount;
    DriverBase = MiPteToAddress(PointerPte);

    /* The driver is here */
    *ImageBase = DriverBase;
    DPRINT1("Loading: %wZ at %p with %lx pages\n", FileName, DriverBase, PteCount);

    /* Lock the PFN database */
    OldIrql = MiAcquirePfnLock();

    /* Loop the new driver PTEs */
    TempPte = ValidKernelPte;
    while (PointerPte < LastPte)
    {
        /* Make sure the PTE is not valid for whatever reason */
        ASSERT(PointerPte->u.Hard.Valid == 0);

        /* Some debug stuff */
        MI_SET_USAGE(MI_USAGE_DRIVER_PAGE);
#if MI_TRACE_PFNS
        if (FileName->Buffer)
        {
            PWCHAR pos = NULL;
            ULONG len = 0;
            pos = wcsrchr(FileName->Buffer, '\\');
            len = wcslen(pos) * sizeof(WCHAR);
            if (pos) snprintf(MI_PFN_CURRENT_PROCESS_NAME, min(16, len), "%S", pos);
        }
#endif

        /* Grab a page */
        PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());

        /* Initialize its PFN entry */
        MiInitializePfn(PageFrameIndex, PointerPte, TRUE);

        /* Write the PTE */
        TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
        MI_WRITE_VALID_PTE(PointerPte, TempPte);

        /* Move on */
        PointerPte++;
    }

    /* Release the PFN lock */
    MiReleasePfnLock(OldIrql);

    /* Copy the image */
    RtlCopyMemory(DriverBase, Base, PteCount << PAGE_SHIFT);

    /* Now unmap the view */
    Status = MmUnmapViewOfSection(Process, Base);
    ASSERT(NT_SUCCESS(Status));

    /* Detach and return status */
    KeUnstackDetachProcess(&ApcState);
    return Status;
}

PVOID
NTAPI
MiLocateExportName(IN PVOID DllBase,
                   IN PCHAR ExportName)
{
    PULONG NameTable;
    PUSHORT OrdinalTable;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
    LONG Low = 0, Mid = 0, High, Ret;
    USHORT Ordinal;
    PVOID Function;
    ULONG ExportSize;
    PULONG ExportTable;
    PAGED_CODE();

    /* Get the export directory */
    ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
                                                   TRUE,
                                                   IMAGE_DIRECTORY_ENTRY_EXPORT,
                                                   &ExportSize);
    if (!ExportDirectory) return NULL;

    /* Setup name tables */
    NameTable = (PULONG)((ULONG_PTR)DllBase +
                         ExportDirectory->AddressOfNames);
    OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
                             ExportDirectory->AddressOfNameOrdinals);

    /* Do a binary search */
    High = ExportDirectory->NumberOfNames - 1;
    while (High >= Low)
    {
        /* Get new middle value */
        Mid = (Low + High) >> 1;

        /* Compare name */
        Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
        if (Ret < 0)
        {
            /* Update high */
            High = Mid - 1;
        }
        else if (Ret > 0)
        {
            /* Update low */
            Low = Mid + 1;
        }
        else
        {
            /* We got it */
            break;
        }
    }

    /* Check if we couldn't find it */
    if (High < Low) return NULL;

    /* Otherwise, this is the ordinal */
    Ordinal = OrdinalTable[Mid];

    /* Resolve the address and write it */
    ExportTable = (PULONG)((ULONG_PTR)DllBase +
                           ExportDirectory->AddressOfFunctions);
    Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);

    /* Check if the function is actually a forwarder */
    if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
        ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
    {
        /* It is, fail */
        return NULL;
    }

    /* We found it */
    return Function;
}

NTSTATUS
NTAPI
MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
                    IN PLIST_ENTRY ListHead)
{
    UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
        L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
    PMM_DLL_INITIALIZE DllInit;
    UNICODE_STRING RegPath, ImportName;
    NTSTATUS Status;

    /* Try to see if the image exports a DllInitialize routine */
    DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
                                                     "DllInitialize");
    if (!DllInit) return STATUS_SUCCESS;

    /*
     * Do a temporary copy of BaseDllName called ImportName
     * because we'll alter the length of the string.
     */
    ImportName.Length = LdrEntry->BaseDllName.Length;
    ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
    ImportName.Buffer = LdrEntry->BaseDllName.Buffer;

    /* Obtain the path to this dll's service in the registry */
    RegPath.MaximumLength = ServicesKeyName.Length +
        ImportName.Length + sizeof(UNICODE_NULL);
    RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
                                           RegPath.MaximumLength,
                                           TAG_LDR_WSTR);

    /* Check if this allocation was unsuccessful */
    if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES;

    /* Build and append the service name itself */
    RegPath.Length = ServicesKeyName.Length;
    RtlCopyMemory(RegPath.Buffer,
                  ServicesKeyName.Buffer,
                  ServicesKeyName.Length);

    /* Check if there is a dot in the filename */
    if (wcschr(ImportName.Buffer, L'.'))
    {
        /* Remove the extension */
        ImportName.Length = (USHORT)(wcschr(ImportName.Buffer, L'.') -
            ImportName.Buffer) * sizeof(WCHAR);
    }

    /* Append service name (the basename without extension) */
    RtlAppendUnicodeStringToString(&RegPath, &ImportName);

    /* Now call the DllInit func */
    DPRINT("Calling DllInit(%wZ)\n", &RegPath);
    Status = DllInit(&RegPath);

    /* Clean up */
    ExFreePoolWithTag(RegPath.Buffer, TAG_LDR_WSTR);

    /* Return status value which DllInitialize returned */
    return Status;
}

BOOLEAN
NTAPI
MiCallDllUnloadAndUnloadDll(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
    NTSTATUS Status;
    PMM_DLL_UNLOAD Func;
    PAGED_CODE();

    /* Get the unload routine */
    Func = (PMM_DLL_UNLOAD)MiLocateExportName(LdrEntry->DllBase, "DllUnload");
    if (!Func) return FALSE;

    /* Call it and check for success */
    Status = Func();
    if (!NT_SUCCESS(Status)) return FALSE;

    /* Lie about the load count so we can unload the image */
    ASSERT(LdrEntry->LoadCount == 0);
    LdrEntry->LoadCount = 1;

    /* Unload it and return true */
    MmUnloadSystemImage(LdrEntry);
    return TRUE;
}

NTSTATUS
NTAPI
MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
{
    SIZE_T i;
    LOAD_IMPORTS SingleEntry;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    PVOID CurrentImports;
    PAGED_CODE();

    /* Check if there's no imports or if we're a boot driver */
    if ((ImportList == MM_SYSLDR_NO_IMPORTS) ||
        (ImportList == MM_SYSLDR_BOOT_LOADED) ||
        (ImportList->Count == 0))
    {
        /* Then there's nothing to do */
        return STATUS_SUCCESS;
    }

    /* Check for single-entry */
    if ((ULONG_PTR)ImportList & MM_SYSLDR_SINGLE_ENTRY)
    {
        /* Set it up */
        SingleEntry.Count = 1;
        SingleEntry.Entry[0] = (PVOID)((ULONG_PTR)ImportList &~ MM_SYSLDR_SINGLE_ENTRY);

        /* Use this as the import list */
        ImportList = &SingleEntry;
    }

    /* Loop the import list */
    for (i = 0; (i < ImportList->Count) && (ImportList->Entry[i]); i++)
    {
        /* Get the entry */
        LdrEntry = ImportList->Entry[i];
        DPRINT1("%wZ <%wZ>\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName);

        /* Skip boot loaded images */
        if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) continue;

        /* Dereference the entry */
        ASSERT(LdrEntry->LoadCount >= 1);
        if (!--LdrEntry->LoadCount)
        {
            /* Save the import data in case unload fails */
            CurrentImports = LdrEntry->LoadedImports;

            /* This is the last entry */
            LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
            if (MiCallDllUnloadAndUnloadDll(LdrEntry))
            {
                /* Unloading worked, parse this DLL's imports too */
                MiDereferenceImports(CurrentImports);

                /* Check if we had valid imports */
                if ((CurrentImports != MM_SYSLDR_BOOT_LOADED) &&
                    (CurrentImports != MM_SYSLDR_NO_IMPORTS) &&
                    !((ULONG_PTR)CurrentImports & MM_SYSLDR_SINGLE_ENTRY))
                {
                    /* Free them */
                    ExFreePoolWithTag(CurrentImports, TAG_LDR_IMPORTS);
                }
            }
            else
            {
                /* Unload failed, restore imports */
                LdrEntry->LoadedImports = CurrentImports;
            }
        }
    }

    /* Done */
    return STATUS_SUCCESS;
}

VOID
NTAPI
MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
    PAGED_CODE();

    /* Check if there's no imports or we're a boot driver or only one entry */
    if ((LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) ||
        (LdrEntry->LoadedImports == MM_SYSLDR_NO_IMPORTS) ||
        ((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
    {
        /* Nothing to do */
        return;
    }

    /* Otherwise, free the import list */
    ExFreePoolWithTag(LdrEntry->LoadedImports, TAG_LDR_IMPORTS);
    LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
}

PVOID
NTAPI
MiFindExportedRoutineByName(IN PVOID DllBase,
                            IN PANSI_STRING ExportName)
{
    PULONG NameTable;
    PUSHORT OrdinalTable;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
    LONG Low = 0, Mid = 0, High, Ret;
    USHORT Ordinal;
    PVOID Function;
    ULONG ExportSize;
    PULONG ExportTable;
    PAGED_CODE();

    /* Get the export directory */
    ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
                                                   TRUE,
                                                   IMAGE_DIRECTORY_ENTRY_EXPORT,
                                                   &ExportSize);
    if (!ExportDirectory) return NULL;

    /* Setup name tables */
    NameTable = (PULONG)((ULONG_PTR)DllBase +
                         ExportDirectory->AddressOfNames);
    OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
                             ExportDirectory->AddressOfNameOrdinals);

    /* Do a binary search */
    High = ExportDirectory->NumberOfNames - 1;
    while (High >= Low)
    {
        /* Get new middle value */
        Mid = (Low + High) >> 1;

        /* Compare name */
        Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
        if (Ret < 0)
        {
            /* Update high */
            High = Mid - 1;
        }
        else if (Ret > 0)
        {
            /* Update low */
            Low = Mid + 1;
        }
        else
        {
            /* We got it */
            break;
        }
    }

    /* Check if we couldn't find it */
    if (High < Low) return NULL;

    /* Otherwise, this is the ordinal */
    Ordinal = OrdinalTable[Mid];

    /* Validate the ordinal */
    if (Ordinal >= ExportDirectory->NumberOfFunctions) return NULL;

    /* Resolve the address and write it */
    ExportTable = (PULONG)((ULONG_PTR)DllBase +
                           ExportDirectory->AddressOfFunctions);
    Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);

    /* We found it! */
    ASSERT((Function < (PVOID)ExportDirectory) ||
           (Function > (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));

    return Function;
}

VOID
NTAPI
MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
                     IN BOOLEAN Insert)
{
    KIRQL OldIrql;

    /* Acquire module list lock */
    KeEnterCriticalRegion();
    ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);

    /* Acquire the spinlock too as we will insert or remove the entry */
    OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);

    /* Insert or remove from the list */
    if (Insert)
        InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks);
    else
        RemoveEntryList(&LdrEntry->InLoadOrderLinks);

    /* Release locks */
    KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
    ExReleaseResourceLite(&PsLoadedModuleResource);
    KeLeaveCriticalRegion();
}

INIT_FUNCTION
VOID
NTAPI
MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
               IN PVOID OldBase,
               IN PVOID NewBase,
               IN ULONG Size)
{
    ULONG_PTR OldBaseTop, Delta;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    PLIST_ENTRY NextEntry;
    ULONG ImportSize;
    //
    // FIXME: MINGW-W64 must fix LD to generate drivers that Windows can load,
    // since a real version of Windows would fail at this point, but they seem
    // busy implementing features such as "HotPatch" support in GCC 4.6 instead,
    // a feature which isn't even used by Windows. Priorities, priorities...
    // Please note that Microsoft WDK EULA and license prohibits using
    // the information contained within it for the generation of "non-Windows"
    // drivers, which is precisely what LD will generate, since an LD driver
    // will not load on Windows.
    //
#ifdef _WORKING_LINKER_
    ULONG i;
#endif
    PULONG_PTR ImageThunk;
    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;

    /* Calculate the top and delta */
    OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
    Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;

    /* Loop the loader block */
    for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
         NextEntry != &LoaderBlock->LoadOrderListHead;
         NextEntry = NextEntry->Flink)
    {
        /* Get the loader entry */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);
#ifdef _WORKING_LINKER_
        /* Get the IAT */
        ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
                                                  TRUE,
                                                  IMAGE_DIRECTORY_ENTRY_IAT,
                                                  &ImportSize);
        if (!ImageThunk) continue;

        /* Make sure we have an IAT */
        DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
        for (i = 0; i < ImportSize; i++, ImageThunk++)
        {
            /* Check if it's within this module */
            if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
            {
                /* Relocate it */
                DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
                        ImageThunk, *ImageThunk, *ImageThunk + Delta);
                *ImageThunk += Delta;
            }
        }
#else
        /* Get the import table */
        ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
                                                        TRUE,
                                                        IMAGE_DIRECTORY_ENTRY_IMPORT,
                                                        &ImportSize);
        if (!ImportDescriptor) continue;

        /* Make sure we have an IAT */
        DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
        while ((ImportDescriptor->Name) &&
               (ImportDescriptor->OriginalFirstThunk))
        {
            /* Get the image thunk */
            ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
                                 ImportDescriptor->FirstThunk);
            while (*ImageThunk)
            {
                /* Check if it's within this module */
                if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
                {
                    /* Relocate it */
                    DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
                            ImageThunk, *ImageThunk, *ImageThunk + Delta);
                    *ImageThunk += Delta;
                }

                /* Go to the next thunk */
                ImageThunk++;
            }

            /* Go to the next import */
            ImportDescriptor++;
        }
#endif
    }
}

NTSTATUS
NTAPI
MiSnapThunk(IN PVOID DllBase,
            IN PVOID ImageBase,
            IN PIMAGE_THUNK_DATA Name,
            IN PIMAGE_THUNK_DATA Address,
            IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
            IN ULONG ExportSize,
            IN BOOLEAN SnapForwarder,
            OUT PCHAR *MissingApi)
{
    BOOLEAN IsOrdinal;
    USHORT Ordinal;
    PULONG NameTable;
    PUSHORT OrdinalTable;
    PIMAGE_IMPORT_BY_NAME NameImport;
    USHORT Hint;
    ULONG Low = 0, Mid = 0, High;
    LONG Ret;
    NTSTATUS Status;
    PCHAR MissingForwarder;
    CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
    PULONG ExportTable;
    ANSI_STRING DllName;
    UNICODE_STRING ForwarderName;
    PLIST_ENTRY NextEntry;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    ULONG ForwardExportSize;
    PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
    PIMAGE_IMPORT_BY_NAME ForwardName;
    SIZE_T ForwardLength;
    IMAGE_THUNK_DATA ForwardThunk;
    PAGED_CODE();

    /* Check if this is an ordinal */
    IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
    if ((IsOrdinal) && !(SnapForwarder))
    {
        /* Get the ordinal number and set it as missing */
        Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
                           ExportDirectory->Base);
        *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
    }
    else
    {
        /* Get the VA if we don't have to snap */
        if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
        NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;

        /* Copy the procedure name */
        RtlStringCbCopyA(*MissingApi,
                         MAXIMUM_FILENAME_LENGTH,
                         (PCHAR)&NameImport->Name[0]);

        /* Setup name tables */
        DPRINT("Import name: %s\n", NameImport->Name);
        NameTable = (PULONG)((ULONG_PTR)DllBase +
                             ExportDirectory->AddressOfNames);
        OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
                                 ExportDirectory->AddressOfNameOrdinals);

        /* Get the hint and check if it's valid */
        Hint = NameImport->Hint;
        if ((Hint < ExportDirectory->NumberOfNames) &&
            !(strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
        {
            /* We have a match, get the ordinal number from here */
            Ordinal = OrdinalTable[Hint];
        }
        else
        {
            /* Do a binary search */
            High = ExportDirectory->NumberOfNames - 1;
            while (High >= Low)
            {
                /* Get new middle value */
                Mid = (Low + High) >> 1;

                /* Compare name */
                Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
                if (Ret < 0)
                {
                    /* Update high */
                    High = Mid - 1;
                }
                else if (Ret > 0)
                {
                    /* Update low */
                    Low = Mid + 1;
                }
                else
                {
                    /* We got it */
                    break;
                }
            }

            /* Check if we couldn't find it */
            if (High < Low)
            {
                DPRINT1("Warning: Driver failed to load, %s not found\n", NameImport->Name);
                return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
            }

            /* Otherwise, this is the ordinal */
            Ordinal = OrdinalTable[Mid];
        }
    }

    /* Check if the ordinal is invalid */
    if (Ordinal >= ExportDirectory->NumberOfFunctions)
    {
        /* Fail */
        Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
    }
    else
    {
        /* In case the forwarder is missing */
        MissingForwarder = NameBuffer;

        /* Resolve the address and write it */
        ExportTable = (PULONG)((ULONG_PTR)DllBase +
                               ExportDirectory->AddressOfFunctions);
        Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];

        /* Assume success from now on */
        Status = STATUS_SUCCESS;

        /* Check if the function is actually a forwarder */
        if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
            (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
        {
            /* Now assume failure in case the forwarder doesn't exist */
            Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;

            /* Build the forwarder name */
            DllName.Buffer = (PCHAR)Address->u1.Function;
            DllName.Length = (USHORT)(strchr(DllName.Buffer, '.') -
                                      DllName.Buffer) +
                                      sizeof(ANSI_NULL);
            DllName.MaximumLength = DllName.Length;

            /* Convert it */
            if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
                                                         &DllName,
                                                         TRUE)))
            {
                /* We failed, just return an error */
                return Status;
            }

            /* Loop the module list */
            NextEntry = PsLoadedModuleList.Flink;
            while (NextEntry != &PsLoadedModuleList)
            {
                /* Get the loader entry */
                LdrEntry = CONTAINING_RECORD(NextEntry,
                                             LDR_DATA_TABLE_ENTRY,
                                             InLoadOrderLinks);

                /* Check if it matches */
                if (RtlPrefixUnicodeString(&ForwarderName,
                                           &LdrEntry->BaseDllName,
                                           TRUE))
                {
                    /* Get the forwarder export directory */
                    ForwardExportDirectory =
                        RtlImageDirectoryEntryToData(LdrEntry->DllBase,
                                                     TRUE,
                                                     IMAGE_DIRECTORY_ENTRY_EXPORT,
                                                     &ForwardExportSize);
                    if (!ForwardExportDirectory) break;

                    /* Allocate a name entry */
                    ForwardLength = strlen(DllName.Buffer + DllName.Length) +
                                    sizeof(ANSI_NULL);
                    ForwardName = ExAllocatePoolWithTag(PagedPool,
                                                        sizeof(*ForwardName) +
                                                        ForwardLength,
                                                        TAG_LDR_WSTR);
                    if (!ForwardName) break;

                    /* Copy the data */
                    RtlCopyMemory(&ForwardName->Name[0],
                                  DllName.Buffer + DllName.Length,
                                  ForwardLength);
                    ForwardName->Hint = 0;

                    /* Set the new address */
                    ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;

                    /* Snap the forwarder */
                    Status = MiSnapThunk(LdrEntry->DllBase,
                                         ImageBase,
                                         &ForwardThunk,
                                         &ForwardThunk,
                                         ForwardExportDirectory,
                                         ForwardExportSize,
                                         TRUE,
                                         &MissingForwarder);

                    /* Free the forwarder name and set the thunk */
                    ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
                    Address->u1 = ForwardThunk.u1;
                    break;
                }

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

            /* Free the name */
            RtlFreeUnicodeString(&ForwarderName);
        }
    }

    /* Return status */
    return Status;
}

NTSTATUS
NTAPI
MmUnloadSystemImage(IN PVOID ImageHandle)
{
    PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
    PVOID BaseAddress = LdrEntry->DllBase;
    NTSTATUS Status;
    STRING TempName;
    BOOLEAN HadEntry = FALSE;

    /* Acquire the loader lock */
    KeEnterCriticalRegion();
    KeWaitForSingleObject(&MmSystemLoadLock,
                          WrVirtualMemory,
                          KernelMode,
                          FALSE,
                          NULL);

    /* Check if this driver was loaded at boot and didn't get imports parsed */
    if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) goto Done;

    /* We should still be alive */
    ASSERT(LdrEntry->LoadCount != 0);
    LdrEntry->LoadCount--;

    /* Check if we're still loaded */
    if (LdrEntry->LoadCount) goto Done;

    /* We should cleanup... are symbols loaded */
    if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
    {
        /* Create the ANSI name */
        Status = RtlUnicodeStringToAnsiString(&TempName,
                                              &LdrEntry->BaseDllName,
                                              TRUE);
        if (NT_SUCCESS(Status))
        {
            /* Unload the symbols */
            DbgUnLoadImageSymbols(&TempName,
                                  BaseAddress,
                                  (ULONG_PTR)PsGetCurrentProcessId());
            RtlFreeAnsiString(&TempName);
        }
    }

    /* FIXME: Free the driver */
    DPRINT1("Leaking driver: %wZ\n", &LdrEntry->BaseDllName);
    //MmFreeSection(LdrEntry->DllBase);

    /* Check if we're linked in */
    if (LdrEntry->InLoadOrderLinks.Flink)
    {
        /* Remove us */
        MiProcessLoaderEntry(LdrEntry, FALSE);
        HadEntry = TRUE;
    }

    /* Dereference and clear the imports */
    MiDereferenceImports(LdrEntry->LoadedImports);
    MiClearImports(LdrEntry);

    /* Check if the entry needs to go away */
    if (HadEntry)
    {
        /* Check if it had a name */
        if (LdrEntry->FullDllName.Buffer)
        {
            /* Free it */
            ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
        }

        /* Check if we had a section */
        if (LdrEntry->SectionPointer)
        {
            /* Dereference it */
            ObDereferenceObject(LdrEntry->SectionPointer);
        }

        /* Free the entry */
        ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
    }

    /* Release the system lock and return */
Done:
    KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
    KeLeaveCriticalRegion();
    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
MiResolveImageReferences(IN PVOID ImageBase,
                         IN PUNICODE_STRING ImageFileDirectory,
                         IN PUNICODE_STRING NamePrefix OPTIONAL,
                         OUT PCHAR *MissingApi,
                         OUT PWCHAR *MissingDriver,
                         OUT PLOAD_IMPORTS *LoadImports)
{
    static UNICODE_STRING DriversFolderName = RTL_CONSTANT_STRING(L"drivers\\");
    PCHAR MissingApiBuffer = *MissingApi, ImportName;
    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
    ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
    PLOAD_IMPORTS LoadedImports, NewImports;
    ULONG GdiLink, NormalLink, i;
    BOOLEAN ReferenceNeeded, Loaded;
    ANSI_STRING TempString;
    UNICODE_STRING NameString, DllName;
    PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
    PVOID ImportBase, DllBase;
    PLIST_ENTRY NextEntry;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
    NTSTATUS Status;
    PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
    PAGED_CODE();
    DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
           __FUNCTION__, ImageBase, ImageFileDirectory);

    /* No name string buffer yet */
    NameString.Buffer = NULL;

    /* Assume no imports */
    *LoadImports = MM_SYSLDR_NO_IMPORTS;

    /* Get the import descriptor */
    ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
                                                    TRUE,
                                                    IMAGE_DIRECTORY_ENTRY_IMPORT,
                                                    &ImportSize);
    if (!ImportDescriptor) return STATUS_SUCCESS;

    /* Loop all imports to count them */
    for (CurrentImport = ImportDescriptor;
         (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
         CurrentImport++)
    {
        /* One more */
        ImportCount++;
    }

    /* Make sure we have non-zero imports */
    if (ImportCount)
    {
        /* Calculate and allocate the list we'll need */
        LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
        LoadedImports = ExAllocatePoolWithTag(PagedPool,
                                              LoadedImportsSize,
                                              TAG_LDR_IMPORTS);
        if (LoadedImports)
        {
            /* Zero it */
            RtlZeroMemory(LoadedImports, LoadedImportsSize);
            LoadedImports->Count = ImportCount;
        }
    }
    else
    {
        /* No table */
        LoadedImports = NULL;
    }

    /* Reset the import count and loop descriptors again */
    ImportCount = GdiLink = NormalLink = 0;
    while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
    {
        /* Get the name */
        ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);

        /* Check if this is a GDI driver */
        GdiLink = GdiLink |
                  !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));

        /* We can also allow dxapi (for Windows compat, allow IRT and coverage) */
        NormalLink = NormalLink |
                     ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
                      (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
                      (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
                      (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));

        /* Check if this is a valid GDI driver */
        if ((GdiLink) && (NormalLink))
        {
            /* It's not, it's importing stuff it shouldn't be! */
            Status = STATUS_PROCEDURE_NOT_FOUND;
            goto Failure;
        }

        /* Check for user-mode printer or video card drivers, which don't belong */
        if (!(_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
            !(_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
            !(_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
            !(_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
            !(_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
            !(_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)))
        {
            /* This is not kernel code */
            Status = STATUS_PROCEDURE_NOT_FOUND;
            goto Failure;
        }

        /* Check if this is a "core" import, which doesn't get referenced */
        if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
            !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
            !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
        {
            /* Don't reference this */
            ReferenceNeeded = FALSE;
        }
        else
        {
            /* Reference these modules */
            ReferenceNeeded = TRUE;
        }

        /* Now setup a unicode string for the import */
        RtlInitAnsiString(&TempString, ImportName);
        Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
        if (!NT_SUCCESS(Status))
        {
            /* Failed */
            goto Failure;
        }

        /* We don't support name prefixes yet */
        if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");

        /* Remember that we haven't loaded the import at this point */
CheckDllState:
        Loaded = FALSE;
        ImportBase = NULL;

        /* Loop the driver list */
        NextEntry = PsLoadedModuleList.Flink;
        while (NextEntry != &PsLoadedModuleList)
        {
            /* Get the loader entry and compare the name */
            LdrEntry = CONTAINING_RECORD(NextEntry,
                                         LDR_DATA_TABLE_ENTRY,
                                         InLoadOrderLinks);
            if (RtlEqualUnicodeString(&NameString,
                                      &LdrEntry->BaseDllName,
                                      TRUE))
            {
                /* Get the base address */
                ImportBase = LdrEntry->DllBase;

                /* Check if we haven't loaded yet, and we need references */
                if (!(Loaded) && (ReferenceNeeded))
                {
                    /* Make sure we're not already loading */
                    if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
                    {
                        /* Increase the load count */
                        LdrEntry->LoadCount++;
                    }
                }

                /* Done, break out */
                break;
            }

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

        /* Check if we haven't loaded the import yet */
        if (!ImportBase)
        {
            /* Setup the import DLL name */
            DllName.MaximumLength = NameString.Length +
                                    ImageFileDirectory->Length +
                                    sizeof(UNICODE_NULL);
            DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
                                                   DllName.MaximumLength,
                                                   TAG_LDR_WSTR);
            if (!DllName.Buffer)
            {
                /* We're out of resources */
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto Failure;
            }

            /* Add the import name to the base directory */
            RtlCopyUnicodeString(&DllName, ImageFileDirectory);
            RtlAppendUnicodeStringToString(&DllName,
                                           &NameString);

            /* Load the image */
            Status = MmLoadSystemImage(&DllName,
                                       NamePrefix,
                                       NULL,
                                       FALSE,
                                       (PVOID *)&DllEntry,
                                       &DllBase);

            /* win32k / GDI drivers can also import from system32 folder */
            if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) &&
                (MI_IS_SESSION_ADDRESS(ImageBase) || 1)) // HACK
            {
                /* Free the old name buffer */
                ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);

                /* Calculate size for a string the adds 'drivers\' */
                DllName.MaximumLength += DriversFolderName.Length;

                /* Allocate the new buffer */
                DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
                                                       DllName.MaximumLength,
                                                       TAG_LDR_WSTR);
                if (!DllName.Buffer)
                {
                    /* We're out of resources */
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    goto Failure;
                }

                /* Copy image directory and append 'drivers\' folder name */
                RtlCopyUnicodeString(&DllName, ImageFileDirectory);
                RtlAppendUnicodeStringToString(&DllName, &DriversFolderName);

                /* Now add the import name */
                RtlAppendUnicodeStringToString(&DllName, &NameString);

                /* Try once again to load the image */
                Status = MmLoadSystemImage(&DllName,
                                           NamePrefix,
                                           NULL,
                                           FALSE,
                                           (PVOID *)&DllEntry,
                                           &DllBase);
            }

            if (!NT_SUCCESS(Status))
            {
                /* Fill out the information for the error */
                *MissingDriver = DllName.Buffer;
                *(PULONG)MissingDriver |= 1;
                *MissingApi = NULL;

                DPRINT1("Failed to load dependency: %wZ\n", &DllName);

                /* Don't free the name */
                DllName.Buffer = NULL;

                /* Cleanup and return */
                goto Failure;
            }

            /* We can free the DLL Name */
            ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);
            DllName.Buffer = NULL;

            /* We're now loaded */
            Loaded = TRUE;

            /* Sanity check */
            ASSERT(DllBase == DllEntry->DllBase);

            /* Call the initialization routines */
            Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
            if (!NT_SUCCESS(Status))
            {
                /* We failed, unload the image */
                MmUnloadSystemImage(DllEntry);
                ERROR_DBGBREAK("MmCallDllInitialize failed with status 0x%x\n", Status);
                Loaded = FALSE;
            }

            /* Loop again to make sure that everything is OK */
            goto CheckDllState;
        }

        /* Check if we're support to reference this import */
        if ((ReferenceNeeded) && (LoadedImports))
        {
            /* Make sure we're not already loading */
            if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
            {
                /* Add the entry */
                LoadedImports->Entry[ImportCount] = LdrEntry;
                ImportCount++;
            }
        }

        /* Free the import name */
        RtlFreeUnicodeString(&NameString);

        /* Set the missing driver name and get the export directory */
        *MissingDriver = LdrEntry->BaseDllName.Buffer;
        ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
                                                       TRUE,
                                                       IMAGE_DIRECTORY_ENTRY_EXPORT,
                                                       &ExportSize);
        if (!ExportDirectory)
        {
            /* Cleanup and return */
            DPRINT1("Warning: Driver failed to load, %S not found\n", *MissingDriver);
            Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
            goto Failure;
        }

        /* Make sure we have an IAT */
        if (ImportDescriptor->OriginalFirstThunk)
        {
            /* Get the first thunks */
            OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
                                ImportDescriptor->OriginalFirstThunk);
            FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
                                 ImportDescriptor->FirstThunk);

            /* Loop the IAT */
            while (OrigThunk->u1.AddressOfData)
            {
                /* Snap thunk */
                Status = MiSnapThunk(ImportBase,
                                     ImageBase,
                                     OrigThunk++,
                                     FirstThunk++,
                                     ExportDirectory,
                                     ExportSize,
                                     FALSE,
                                     MissingApi);
                if (!NT_SUCCESS(Status))
                {
                    /* Cleanup and return */
                    goto Failure;
                }

                /* Reset the buffer */
                *MissingApi = MissingApiBuffer;
            }
        }

        /* Go to the next import */
        ImportDescriptor++;
    }

    /* Check if we have an import list */
    if (LoadedImports)
    {
        /* Reset the count again, and loop entries */
        ImportCount = 0;
        for (i = 0; i < LoadedImports->Count; i++)
        {
            if (LoadedImports->Entry[i])
            {
                /* Got an entry, OR it with 1 in case it's the single entry */
                ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] |
                                      MM_SYSLDR_SINGLE_ENTRY);
                ImportCount++;
            }
        }

        /* Check if we had no imports */
        if (!ImportCount)
        {
            /* Free the list and set it to no imports */
            ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
            LoadedImports = MM_SYSLDR_NO_IMPORTS;
        }
        else if (ImportCount == 1)
        {
            /* Just one entry, we can free the table and only use our entry */
            ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
            LoadedImports = (PLOAD_IMPORTS)ImportEntry;
        }
        else if (ImportCount != LoadedImports->Count)
        {
            /* Allocate a new list */
            LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
            NewImports = ExAllocatePoolWithTag(PagedPool,
                                               LoadedImportsSize,
                                               TAG_LDR_IMPORTS);
            if (NewImports)
            {
                /* Set count */
                NewImports->Count = 0;

                /* Loop all the imports */
                for (i = 0; i < LoadedImports->Count; i++)
                {
                    /* Make sure it's valid */
                    if (LoadedImports->Entry[i])
                    {
                        /* Copy it */
                        NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
                        NewImports->Count++;
                    }
                }

                /* Free the old copy */
                ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
                LoadedImports = NewImports;
            }
        }

        /* Return the list */
        *LoadImports = LoadedImports;
    }

    /* Return success */
    return STATUS_SUCCESS;

Failure:

    /* Cleanup and return */
    RtlFreeUnicodeString(&NameString);

    if (LoadedImports)
    {
        MiDereferenceImports(LoadedImports);
        ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
    }

    return Status;
}

VOID
NTAPI
MiFreeInitializationCode(IN PVOID InitStart,
                         IN PVOID InitEnd)
{
    PMMPTE PointerPte;
    PFN_NUMBER PagesFreed;

    /* Get the start PTE */
    PointerPte = MiAddressToPte(InitStart);
    ASSERT(MI_IS_PHYSICAL_ADDRESS(InitStart) == FALSE);

    /*  Compute the number of pages we expect to free */
    PagesFreed = (PFN_NUMBER)(MiAddressToPte(InitEnd) - PointerPte);

    /* Try to actually free them */
    PagesFreed = MiDeleteSystemPageableVm(PointerPte,
                                          PagesFreed,
                                          0,
                                          NULL);
}

INIT_FUNCTION
VOID
NTAPI
MiFindInitializationCode(OUT PVOID *StartVa,
                         OUT PVOID *EndVa)
{
    ULONG Size, SectionCount, Alignment;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    ULONG_PTR DllBase, InitStart, InitEnd, ImageEnd, InitCode;
    PLIST_ENTRY NextEntry;
    PIMAGE_NT_HEADERS NtHeader;
    PIMAGE_SECTION_HEADER Section, LastSection, InitSection;
    BOOLEAN InitFound;
    DBG_UNREFERENCED_LOCAL_VARIABLE(InitSection);

    /* So we don't free our own code yet */
    InitCode = (ULONG_PTR)&MiFindInitializationCode;

    /* Assume failure */
    *StartVa = NULL;

    /* Enter a critical region while we loop the list */
    KeEnterCriticalRegion();

    /* Loop all loaded modules */
    NextEntry = PsLoadedModuleList.Flink;
    while (NextEntry != &PsLoadedModuleList)
    {
        /* Get the loader entry and its DLL base */
        LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
        DllBase = (ULONG_PTR)LdrEntry->DllBase;

        /* Only process boot loaded images. Other drivers are processed by
           MmFreeDriverInitialization */
        if (LdrEntry->Flags & LDRP_MM_LOADED)
        {
            /* Keep going */
            NextEntry = NextEntry->Flink;
            continue;
        }

        /* Get the NT header */
        NtHeader = RtlImageNtHeader((PVOID)DllBase);
        if (!NtHeader)
        {
            /* Keep going */
            NextEntry = NextEntry->Flink;
            continue;
        }

        /* Get the first section, the section count, and scan them all */
        Section = IMAGE_FIRST_SECTION(NtHeader);
        SectionCount = NtHeader->FileHeader.NumberOfSections;
        InitStart = 0;
        while (SectionCount > 0)
        {
            /* Assume failure */
            InitFound = FALSE;

            /* Is this the INIT section or a discardable section? */
            if ((strncmp((PCCH)Section->Name, "INIT", 5) == 0) ||
                ((Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)))
            {
                /* Remember this */
                InitFound = TRUE;
                InitSection = Section;
            }

            if (InitFound)
            {
                /* Pick the biggest size -- either raw or virtual */
                Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);

                /* Read the section alignment */
                Alignment = NtHeader->OptionalHeader.SectionAlignment;

                /* Get the start and end addresses */
                InitStart = DllBase + Section->VirtualAddress;
                InitEnd = ALIGN_UP_BY(InitStart + Size, Alignment);

                /* Align the addresses to PAGE_SIZE */
                InitStart = ALIGN_UP_BY(InitStart, PAGE_SIZE);
                InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);

                /* Have we reached the last section? */
                if (SectionCount == 1)
                {
                    /* Remember this */
                    LastSection = Section;
                }
                else
                {
                    /* We have not, loop all the sections */
                    LastSection = NULL;
                    do
                    {
                        /* Keep going until we find a non-discardable section range */
                        SectionCount--;
                        Section++;
                        if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
                        {
                            /* Discardable, so record it, then keep going */
                            LastSection = Section;
                        }
                        else
                        {
                            /* Non-contigous discard flag, or no flag, break out */
                            break;
                        }
                    }
                    while (SectionCount > 1);
                }

                /* Have we found a discardable or init section? */
                if (LastSection)
                {
                    /* Pick the biggest size -- either raw or virtual */
                    Size = max(LastSection->SizeOfRawData, LastSection->Misc.VirtualSize);

                    /* Use this as the end of the section address */
                    InitEnd = DllBase + LastSection->VirtualAddress + Size;

                    /* Have we reached the last section yet? */
                    if (SectionCount != 1)
                    {
                        /* Then align this accross the session boundary */
                        InitEnd = ALIGN_UP_BY(InitEnd, Alignment);
                        InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
                    }
                }

                /* Make sure we don't let the init section go past the image */
                ImageEnd = DllBase + LdrEntry->SizeOfImage;
                if (InitEnd > ImageEnd) InitEnd = ALIGN_UP_BY(ImageEnd, PAGE_SIZE);

                /* Make sure we have a valid, non-zero init section */
                if (InitStart < InitEnd)
                {
                    /* Make sure we are not within this code itself */
                    if ((InitCode >= InitStart) && (InitCode < InitEnd))
                    {
                        /* Return it, we can't free ourselves now */
                        ASSERT(*StartVa == 0);
                        *StartVa = (PVOID)InitStart;
                        *EndVa = (PVOID)InitEnd;
                    }
                    else
                    {
                        /* This isn't us -- go ahead and free it */
                        ASSERT(MI_IS_PHYSICAL_ADDRESS((PVOID)InitStart) == FALSE);
                        DPRINT("Freeing init code: %p-%p ('%wZ' @%p : '%s')\n",
                               (PVOID)InitStart,
                               (PVOID)InitEnd,
                               &LdrEntry->BaseDllName,
                               LdrEntry->DllBase,
                               InitSection->Name);
                        MiFreeInitializationCode((PVOID)InitStart, (PVOID)InitEnd);
                    }
                }
            }

            /* Move to the next section */
            SectionCount--;
            Section++;
        }

        /* Move to the next module */
        NextEntry = NextEntry->Flink;
    }

    /* Leave the critical region and return */
    KeLeaveCriticalRegion();
}

/*
 * Note: This function assumes that all discardable sections are at the end of
 * the PE file. It searches backwards until it finds the non-discardable section
 */
VOID
NTAPI
MmFreeDriverInitialization(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
    PMMPTE StartPte, EndPte;
    PFN_NUMBER PageCount;
    PVOID DllBase;
    ULONG i;
    PIMAGE_NT_HEADERS NtHeader;
    PIMAGE_SECTION_HEADER Section, DiscardSection;

    /* Get the base address and the page count */
    DllBase = LdrEntry->DllBase;
    PageCount = LdrEntry->SizeOfImage >> PAGE_SHIFT;

    /* Get the last PTE in this image */
    EndPte = MiAddressToPte(DllBase) + PageCount;

    /* Get the NT header */
    NtHeader = RtlImageNtHeader(DllBase);
    if (!NtHeader) return;

    /* Get the last section and loop each section backwards */
    Section = IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections;
    DiscardSection = NULL;
    for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
    {
        /* Go back a section and check if it's discardable */
        Section--;
        if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
        {
            /* It is, select it for freeing */
            DiscardSection = Section;
        }
        else
        {
            /* No more discardable sections exist, bail out */
            break;
        }
    }

    /* Bail out if there's nothing to free */
    if (!DiscardSection) return;

    /* Push the DLL base to the first disacrable section, and get its PTE */
    DllBase = (PVOID)ROUND_TO_PAGES((ULONG_PTR)DllBase + DiscardSection->VirtualAddress);
    ASSERT(MI_IS_PHYSICAL_ADDRESS(DllBase) == FALSE);
    StartPte = MiAddressToPte(DllBase);

    /* Check how many pages to free total */
    PageCount = (PFN_NUMBER)(EndPte - StartPte);
    if (!PageCount) return;

    /* Delete this many PTEs */
    MiDeleteSystemPageableVm(StartPte, PageCount, 0, NULL);
}

INIT_FUNCTION
VOID
NTAPI
MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    PLIST_ENTRY NextEntry;
    ULONG i = 0;
    PIMAGE_NT_HEADERS NtHeader;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    PIMAGE_FILE_HEADER FileHeader;
    BOOLEAN ValidRelocs;
    PIMAGE_DATA_DIRECTORY DataDirectory;
    PVOID DllBase, NewImageAddress;
    NTSTATUS Status;
    PMMPTE PointerPte, StartPte, LastPte;
    PFN_COUNT PteCount;
    PMMPFN Pfn1;
    MMPTE TempPte, OldPte;

    /* Loop driver list */
    for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
         NextEntry != &LoaderBlock->LoadOrderListHead;
         NextEntry = NextEntry->Flink)
    {
        /* Get the loader entry and NT header */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);
        NtHeader = RtlImageNtHeader(LdrEntry->DllBase);

        /* Debug info */
        DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
                LdrEntry->DllBase,
                (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
                &LdrEntry->FullDllName);

        /* Get the first PTE and the number of PTEs we'll need */
        PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
        PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
        LastPte = StartPte + PteCount;

#if MI_TRACE_PFNS
        /* Loop the PTEs */
        while (PointerPte < LastPte)
        {
            ULONG len;
            ASSERT(PointerPte->u.Hard.Valid == 1);
            Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
            len = wcslen(LdrEntry->BaseDllName.Buffer) * sizeof(WCHAR);
            snprintf(Pfn1->ProcessName, min(16, len), "%S", LdrEntry->BaseDllName.Buffer);
            PointerPte++;
        }
#endif
        /* Skip kernel and HAL */
        /* ROS HACK: Skip BOOTVID/KDCOM too */
        i++;
        if (i <= 4) continue;

        /* Skip non-drivers */
        if (!NtHeader) continue;

        /* Get the file header and make sure we can relocate */
        FileHeader = &NtHeader->FileHeader;
        if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
        if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
            IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;

        /* Everything made sense until now, check the relocation section too */
        DataDirectory = &NtHeader->OptionalHeader.
                        DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
        if (!DataDirectory->VirtualAddress)
        {
            /* We don't really have relocations */
            ValidRelocs = FALSE;
        }
        else
        {
            /* Make sure the size is valid */
            if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
                LdrEntry->SizeOfImage)
            {
                /* They're not, skip */
                continue;
            }

            /* We have relocations */
            ValidRelocs = TRUE;
        }

        /* Remember the original address */
        DllBase = LdrEntry->DllBase;

        /* Loop the PTEs */
        PointerPte = StartPte;
        while (PointerPte < LastPte)
        {
            /* Mark the page modified in the PFN database */
            ASSERT(PointerPte->u.Hard.Valid == 1);
            Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
            ASSERT(Pfn1->u3.e1.Rom == 0);
            Pfn1->u3.e1.Modified = TRUE;

            /* Next */
            PointerPte++;
        }

        /* Now reserve system PTEs for the image */
        PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
        if (!PointerPte)
        {
            /* Shouldn't happen */
            ERROR_FATAL("[Mm0]: Couldn't allocate driver section!\n");
            return;
        }

        /* This is the new virtual address for the module */
        LastPte = PointerPte + PteCount;
        NewImageAddress = MiPteToAddress(PointerPte);

        /* Sanity check */
        DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
        ASSERT(ExpInitializationPhase == 0);

        /* Loop the new driver PTEs */
        TempPte = ValidKernelPte;
        while (PointerPte < LastPte)
        {
            /* Copy the old data */
            OldPte = *StartPte;
            ASSERT(OldPte.u.Hard.Valid == 1);

            /* Set page number from the loader's memory */
            TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;

            /* Write it */
            MI_WRITE_VALID_PTE(PointerPte, TempPte);

            /* Move on */
            PointerPte++;
            StartPte++;
        }

        /* Update position */
        PointerPte -= PteCount;

        /* Sanity check */
        ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);

        /* Set the image base to the address where the loader put it */
        NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;

        /* Check if we had relocations */
        if (ValidRelocs)
        {
            /* Relocate the image */
            Status = LdrRelocateImageWithBias(NewImageAddress,
                                              0,
                                              "SYSLDR",
                                              STATUS_SUCCESS,
                                              STATUS_CONFLICTING_ADDRESSES,
                                              STATUS_INVALID_IMAGE_FORMAT);
            if (!NT_SUCCESS(Status))
            {
                /* This shouldn't happen */
                ERROR_FATAL("Relocations failed!\n");
                return;
            }
        }

        /* Update the loader entry */
        LdrEntry->DllBase = NewImageAddress;

        /* Update the thunks */
        DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
        MiUpdateThunks(LoaderBlock,
                       DllBase,
                       NewImageAddress,
                       LdrEntry->SizeOfImage);

        /* Update the loader entry */
        LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
        LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
                                NtHeader->OptionalHeader.AddressOfEntryPoint);
        LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;

        /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
    }
}

INIT_FUNCTION
NTSTATUS
NTAPI
MiBuildImportsForBootDrivers(VOID)
{
    PLIST_ENTRY NextEntry, NextEntry2;
    PLDR_DATA_TABLE_ENTRY LdrEntry, KernelEntry, HalEntry, LdrEntry2, LastEntry;
    PLDR_DATA_TABLE_ENTRY* EntryArray;
    UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
    UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
    PLOAD_IMPORTS LoadedImports;
    ULONG LoadedImportsSize, ImportSize;
    PULONG_PTR ImageThunk;
    ULONG_PTR DllBase, DllEnd;
    ULONG Modules = 0, i, j = 0;
    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;

    /* Initialize variables */
    KernelEntry = HalEntry = LastEntry = NULL;

    /* Loop the loaded module list... we are early enough that no lock is needed */
    NextEntry = PsLoadedModuleList.Flink;
    while (NextEntry != &PsLoadedModuleList)
    {
        /* Get the entry */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);

        /* Check if it's the kernel or HAL */
        if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
        {
            /* Found it */
            KernelEntry = LdrEntry;
        }
        else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
        {
            /* Found it */
            HalEntry = LdrEntry;
        }

        /* Check if this is a driver DLL */
        if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
        {
            /* Check if this is the HAL or kernel */
            if ((LdrEntry == HalEntry) || (LdrEntry == KernelEntry))
            {
                /* Add a reference */
                LdrEntry->LoadCount = 1;
            }
            else
            {
                /* No referencing needed */
                LdrEntry->LoadCount = 0;
            }
        }
        else
        {
            /* Add a reference for all other modules as well */
            LdrEntry->LoadCount = 1;
        }

        /* Remember this came from the loader */
        LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;

        /* Keep looping */
        NextEntry = NextEntry->Flink;
        Modules++;
    }

    /* We must have at least found the kernel and HAL */
    if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;

    /* Allocate the list */
    EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), TAG_LDR_IMPORTS);
    if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;

    /* Loop the loaded module list again */
    NextEntry = PsLoadedModuleList.Flink;
    while (NextEntry != &PsLoadedModuleList)
    {
        /* Get the entry */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);
#ifdef _WORKING_LOADER_
        /* Get its imports */
        ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
                                                  TRUE,
                                                  IMAGE_DIRECTORY_ENTRY_IAT,
                                                  &ImportSize);
        if (!ImageThunk)
#else
        /* Get its imports */
        ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
                                                        TRUE,
                                                        IMAGE_DIRECTORY_ENTRY_IMPORT,
                                                        &ImportSize);
        if (!ImportDescriptor)
#endif
        {
            /* None present */
            LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
            NextEntry = NextEntry->Flink;
            continue;
        }

        /* Clear the list and count the number of IAT thunks */
        RtlZeroMemory(EntryArray, Modules * sizeof(PVOID));
#ifdef _WORKING_LOADER_
        ImportSize /= sizeof(ULONG_PTR);

        /* Scan the thunks */
        for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
#else
        DllBase = DllEnd = i = 0;
        while ((ImportDescriptor->Name) &&
               (ImportDescriptor->OriginalFirstThunk))
        {
            /* Get the image thunk */
            ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
                                 ImportDescriptor->FirstThunk);
            while (*ImageThunk)
#endif
            {
            /* Do we already have an address? */
            if (DllBase)
            {
                /* Is the thunk in the same address? */
                if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
                {
                    /* Skip it, we already have a reference for it */
                    ASSERT(EntryArray[j]);
                    ImageThunk++;
                    continue;
                }
            }

            /* Loop the loaded module list to locate this address owner */
            j = 0;
            NextEntry2 = PsLoadedModuleList.Flink;
            while (NextEntry2 != &PsLoadedModuleList)
            {
                /* Get the entry */
                LdrEntry2 = CONTAINING_RECORD(NextEntry2,
                                              LDR_DATA_TABLE_ENTRY,
                                              InLoadOrderLinks);

                /* Get the address range for this module */
                DllBase = (ULONG_PTR)LdrEntry2->DllBase;
                DllEnd = DllBase + LdrEntry2->SizeOfImage;

                /* Check if this IAT entry matches it */
                if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
                {
                    /* Save it */
                    //DPRINT1("Found imported dll: %wZ\n", &LdrEntry2->BaseDllName);
                    EntryArray[j] = LdrEntry2;
                    break;
                }

                /* Keep searching */
                NextEntry2 = NextEntry2->Flink;
                j++;
            }

            /* Do we have a thunk outside the range? */
            if ((*ImageThunk < DllBase) || (*ImageThunk >= DllEnd))
            {
                /* Could be 0... */
                if (*ImageThunk)
                {
                    /* Should not be happening */
                    ERROR_FATAL("Broken IAT entry for %p at %p (%lx)\n",
                                LdrEntry, ImageThunk, *ImageThunk);
                }

                /* Reset if we hit this */
                DllBase = 0;
            }
#ifndef _WORKING_LOADER_
            ImageThunk++;
            }

            i++;
            ImportDescriptor++;
#endif
        }

        /* Now scan how many imports we really have */
        for (i = 0, ImportSize = 0; i < Modules; i++)
        {
            /* Skip HAL and kernel */
            if ((EntryArray[i]) &&
                (EntryArray[i] != HalEntry) &&
                (EntryArray[i] != KernelEntry))
            {
                /* A valid reference */
                LastEntry = EntryArray[i];
                ImportSize++;
            }
        }

        /* Do we have any imports after all? */
        if (!ImportSize)
        {
            /* No */
            LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
        }
        else if (ImportSize == 1)
        {
            /* A single entry import */
            LdrEntry->LoadedImports = (PVOID)((ULONG_PTR)LastEntry | MM_SYSLDR_SINGLE_ENTRY);
            LastEntry->LoadCount++;
        }
        else
        {
            /* We need an import table */
            LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
            LoadedImports = ExAllocatePoolWithTag(PagedPool,
                                                  LoadedImportsSize,
                                                  TAG_LDR_IMPORTS);
            ASSERT(LoadedImports);

            /* Save the count */
            LoadedImports->Count = ImportSize;

            /* Now copy all imports */
            for (i = 0, j = 0; i < Modules; i++)
            {
                /* Skip HAL and kernel */
                if ((EntryArray[i]) &&
                    (EntryArray[i] != HalEntry) &&
                    (EntryArray[i] != KernelEntry))
                {
                    /* A valid reference */
                    //DPRINT1("Found valid entry: %p\n", EntryArray[i]);
                    LoadedImports->Entry[j] = EntryArray[i];
                    EntryArray[i]->LoadCount++;
                    j++;
                }
            }

            /* Should had as many entries as we expected */
            ASSERT(j == ImportSize);
            LdrEntry->LoadedImports = LoadedImports;
        }

        /* Next */
        NextEntry = NextEntry->Flink;
    }

    /* Free the initial array */
    ExFreePoolWithTag(EntryArray, TAG_LDR_IMPORTS);

    /* FIXME: Might not need to keep the HAL/Kernel imports around */

    /* Kernel and HAL are loaded at boot */
    KernelEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
    HalEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;

    /* All worked well */
    return STATUS_SUCCESS;
}

INIT_FUNCTION
VOID
NTAPI
MiLocateKernelSections(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
    ULONG_PTR DllBase;
    PIMAGE_NT_HEADERS NtHeaders;
    PIMAGE_SECTION_HEADER SectionHeader;
    ULONG Sections, Size;

    /* Get the kernel section header */
    DllBase = (ULONG_PTR)LdrEntry->DllBase;
    NtHeaders = RtlImageNtHeader((PVOID)DllBase);
    SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);

    /* Loop all the sections */
    for (Sections = NtHeaders->FileHeader.NumberOfSections;
         Sections > 0; --Sections, ++SectionHeader)
    {
        /* Grab the size of the section */
        Size = max(SectionHeader->SizeOfRawData, SectionHeader->Misc.VirtualSize);

        /* Check for .RSRC section */
        if (*(PULONG)SectionHeader->Name == 'rsr.')
        {
            /* Remember the PTEs so we can modify them later */
            MiKernelResourceStartPte = MiAddressToPte(DllBase +
                                                      SectionHeader->VirtualAddress);
            MiKernelResourceEndPte = MiAddressToPte(ROUND_TO_PAGES(DllBase +
                                                    SectionHeader->VirtualAddress + Size));
        }
        else if (*(PULONG)SectionHeader->Name == 'LOOP')
        {
            /* POOLCODE vs. POOLMI */
            if (*(PULONG)&SectionHeader->Name[4] == 'EDOC')
            {
                /* Found Ex* Pool code */
                ExPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
                ExPoolCodeEnd = ExPoolCodeStart + Size;
            }
            else if (*(PUSHORT)&SectionHeader->Name[4] == 'MI')
            {
                /* Found Mm* Pool code */
                MmPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
                MmPoolCodeEnd = MmPoolCodeStart + Size;
            }
        }
        else if ((*(PULONG)SectionHeader->Name == 'YSIM') &&
                 (*(PULONG)&SectionHeader->Name[4] == 'ETPS'))
        {
            /* Found MISYSPTE (Mm System PTE code) */
            MmPteCodeStart = DllBase + SectionHeader->VirtualAddress;
            MmPteCodeEnd = MmPteCodeStart + Size;
        }
    }
}

INIT_FUNCTION
BOOLEAN
NTAPI
MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
    PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
    PLIST_ENTRY ListHead, NextEntry;
    ULONG EntrySize;

    /* Setup the loaded module list and locks */
    ExInitializeResourceLite(&PsLoadedModuleResource);
    KeInitializeSpinLock(&PsLoadedModuleSpinLock);
    InitializeListHead(&PsLoadedModuleList);

    /* Get loop variables and the kernel entry */
    ListHead = &LoaderBlock->LoadOrderListHead;
    NextEntry = ListHead->Flink;
    LdrEntry = CONTAINING_RECORD(NextEntry,
                                 LDR_DATA_TABLE_ENTRY,
                                 InLoadOrderLinks);
    PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;

    /* Locate resource section, pool code, and system pte code */
    MiLocateKernelSections(LdrEntry);

    /* Loop the loader block */
    while (NextEntry != ListHead)
    {
        /* Get the loader entry */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);

        /* FIXME: ROS HACK. Make sure this is a driver */
        if (!RtlImageNtHeader(LdrEntry->DllBase))
        {
            /* Skip this entry */
            NextEntry = NextEntry->Flink;
            continue;
        }

        /* Calculate the size we'll need and allocate a copy */
        EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
                    LdrEntry->BaseDllName.MaximumLength +
                    sizeof(UNICODE_NULL);
        NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
        if (!NewEntry) return FALSE;

        /* Copy the entry over */
        *NewEntry = *LdrEntry;

        /* Allocate the name */
        NewEntry->FullDllName.Buffer =
            ExAllocatePoolWithTag(PagedPool,
                                  LdrEntry->FullDllName.MaximumLength +
                                      sizeof(UNICODE_NULL),
                                  TAG_LDR_WSTR);
        if (!NewEntry->FullDllName.Buffer)
        {
            ExFreePoolWithTag(NewEntry, TAG_MODULE_OBJECT);
            return FALSE;
        }

        /* Set the base name */
        NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);

        /* Copy the full and base name */
        RtlCopyMemory(NewEntry->FullDllName.Buffer,
                      LdrEntry->FullDllName.Buffer,
                      LdrEntry->FullDllName.MaximumLength);
        RtlCopyMemory(NewEntry->BaseDllName.Buffer,
                      LdrEntry->BaseDllName.Buffer,
                      LdrEntry->BaseDllName.MaximumLength);

        /* Null-terminate the base name */
        NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
                                     sizeof(WCHAR)] = UNICODE_NULL;

        /* Insert the entry into the list */
        InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
        NextEntry = NextEntry->Flink;
    }

    /* Build the import lists for the boot drivers */
    MiBuildImportsForBootDrivers();

    /* We're done */
    return TRUE;
}

BOOLEAN
NTAPI
MmChangeKernelResourceSectionProtection(IN ULONG_PTR ProtectionMask)
{
    PMMPTE PointerPte;
    MMPTE TempPte;

    /* Don't do anything if the resource section is already writable */
    if (MiKernelResourceStartPte == NULL || MiKernelResourceEndPte == NULL)
        return FALSE;

    /* If the resource section is physical, we cannot change its protection */
    if (MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(MiKernelResourceStartPte)))
        return FALSE;

    /* Loop the PTEs */
    for (PointerPte = MiKernelResourceStartPte; PointerPte < MiKernelResourceEndPte; ++PointerPte)
    {
        /* Read the PTE */
        TempPte = *PointerPte;

        /* Update the protection */
        MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, PointerPte, ProtectionMask, TempPte.u.Hard.PageFrameNumber);
        MI_UPDATE_VALID_PTE(PointerPte, TempPte);
    }

    /* Only flush the current processor's TLB */
    KeFlushCurrentTb();
    return TRUE;
}

VOID
NTAPI
MmMakeKernelResourceSectionWritable(VOID)
{
    /* Don't do anything if the resource section is already writable */
    if (MiKernelResourceStartPte == NULL || MiKernelResourceEndPte == NULL)
        return;

    /* If the resource section is physical, we cannot change its protection */
    if (MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(MiKernelResourceStartPte)))
        return;

    if (MmChangeKernelResourceSectionProtection(MM_READWRITE))
    {
        /*
         * Invalidate the cached resource section PTEs
         * so as to not change its protection again later.
         */
        MiKernelResourceStartPte = NULL;
        MiKernelResourceEndPte = NULL;
    }
}

LOGICAL
NTAPI
MiUseLargeDriverPage(IN ULONG NumberOfPtes,
                     IN OUT PVOID *ImageBaseAddress,
                     IN PUNICODE_STRING BaseImageName,
                     IN BOOLEAN BootDriver)
{
    PLIST_ENTRY NextEntry;
    BOOLEAN DriverFound = FALSE;
    PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
    ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
    ASSERT(*ImageBaseAddress >= MmSystemRangeStart);

#ifdef _X86_
    if (!(KeFeatureBits & KF_LARGE_PAGE)) return FALSE;
    if (!(__readcr4() & CR4_PSE)) return FALSE;
#endif

    /* Make sure there's enough system PTEs for a large page driver */
    if (MmTotalFreeSystemPtes[SystemPteSpace] < (16 * (PDE_MAPPED_VA >> PAGE_SHIFT)))
    {
        return FALSE;
    }

    /* This happens if the registry key had a "*" (wildcard) in it */
    if (MiLargePageAllDrivers == 0)
    {
        /* It didn't, so scan the list */
        NextEntry = MiLargePageDriverList.Flink;
        while (NextEntry != &MiLargePageDriverList)
        {
            /* Check if the driver name matches */
            LargePageDriverEntry = CONTAINING_RECORD(NextEntry,
                                                     MI_LARGE_PAGE_DRIVER_ENTRY,
                                                     Links);
            if (RtlEqualUnicodeString(BaseImageName,
                                      &LargePageDriverEntry->BaseName,
                                      TRUE))
            {
                /* Enable large pages for this driver */
                DriverFound = TRUE;
                break;
            }

            /* Keep trying */
            NextEntry = NextEntry->Flink;
        }

        /* If we didn't find the driver, it doesn't need large pages */
        if (DriverFound == FALSE) return FALSE;
    }

    /* Nothing to do yet */
    DPRINT1("Large pages not supported!\n");
    return FALSE;
}

VOID
NTAPI
MiSetSystemCodeProtection(
    _In_ PMMPTE FirstPte,
    _In_ PMMPTE LastPte,
    _In_ ULONG Protection)
{
    PMMPTE PointerPte;
    MMPTE TempPte;

    /* Loop the PTEs */
    for (PointerPte = FirstPte; PointerPte <= LastPte; PointerPte++)
    {
        /* Read the PTE */
        TempPte = *PointerPte;

        /* Make sure it's valid */
        ASSERT(TempPte.u.Hard.Valid == 1);

        /* Update the protection */
        TempPte.u.Hard.Write = BooleanFlagOn(Protection, IMAGE_SCN_MEM_WRITE);
#if _MI_HAS_NO_EXECUTE
        TempPte.u.Hard.NoExecute = !BooleanFlagOn(Protection, IMAGE_SCN_MEM_EXECUTE);
#endif

        MI_UPDATE_VALID_PTE(PointerPte, TempPte);
    }

    /* Flush it all */
    KeFlushEntireTb(TRUE, TRUE);

    return;
}

VOID
NTAPI
MiWriteProtectSystemImage(
    _In_ PVOID ImageBase)
{
    PIMAGE_NT_HEADERS NtHeaders;
    PIMAGE_SECTION_HEADER SectionHeaders, Section;
    ULONG i;
    PVOID SectionBase, SectionEnd;
    ULONG SectionSize;
    ULONG Protection;
    PMMPTE FirstPte, LastPte;

    /* Check if the registry setting is on or not */
    if (MmEnforceWriteProtection == FALSE)
    {
        /* Ignore section protection */
        return;
    }

    /* Large page mapped images are not supported */
    NT_ASSERT(!MI_IS_PHYSICAL_ADDRESS(ImageBase));

    /* Session images are not yet supported */
    NT_ASSERT(!MI_IS_SESSION_ADDRESS(ImageBase));

    /* Get the NT headers */
    NtHeaders = RtlImageNtHeader(ImageBase);
    if (NtHeaders == NULL)
    {
        DPRINT1("Failed to get NT headers for image @ %p\n", ImageBase);
        return;
    }

    /* Don't touch NT4 drivers */
    if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) ||
        (NtHeaders->OptionalHeader.MajorSubsystemVersion < 5))
    {
        DPRINT1("Skipping NT 4 driver @ %p\n", ImageBase);
        return;
    }

    /* Get the section headers */
    SectionHeaders = IMAGE_FIRST_SECTION(NtHeaders);

    /* Get the base address of the first section */
    SectionBase = Add2Ptr(ImageBase, SectionHeaders[0].VirtualAddress);

    /* Start protecting the image header as R/O */
    FirstPte = MiAddressToPte(ImageBase);
    LastPte = MiAddressToPte(SectionBase) - 1;
    Protection = IMAGE_SCN_MEM_READ;
    if (LastPte >= FirstPte)
    {
        MiSetSystemCodeProtection(FirstPte, LastPte, IMAGE_SCN_MEM_READ);
    }

    /* Loop the sections */
    for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
    {
        /* Get the section base address and size */
        Section = &SectionHeaders[i];
        SectionBase = Add2Ptr(ImageBase, Section->VirtualAddress);
        SectionSize = max(Section->SizeOfRawData, Section->Misc.VirtualSize);

        /* Get the first PTE of this section */
        FirstPte = MiAddressToPte(SectionBase);

        /* Check for overlap with the previous range */
        if (FirstPte == LastPte)
        {
            /* Combine the old and new protection by ORing them */
            Protection |= (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);

            /* Update the protection for this PTE */
            MiSetSystemCodeProtection(FirstPte, FirstPte, Protection);

            /* Skip this PTE */
            FirstPte++;
        }

        /* There can not be gaps! */
        NT_ASSERT(FirstPte == (LastPte + 1));

        /* Get the end of the section and the last PTE */
        SectionEnd = Add2Ptr(SectionBase, SectionSize - 1);
        NT_ASSERT(SectionEnd < Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage));
        LastPte = MiAddressToPte(SectionEnd);

        /* If there are no more pages (after an overlap), skip this section */
        if (LastPte < FirstPte)
        {
            NT_ASSERT(FirstPte == (LastPte + 1));
            continue;
        }

        /* Get the section protection */
        Protection = (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);

        /* Update the protection for this section */
        MiSetSystemCodeProtection(FirstPte, LastPte, Protection);
    }

    /* Image should end with the last section */
    if (ALIGN_UP_POINTER_BY(SectionEnd, PAGE_SIZE) !=
        Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage))
    {
        DPRINT1("ImageBase 0x%p ImageSize 0x%lx Section %u VA 0x%lx Raw 0x%lx virt 0x%lx\n",
            ImageBase,
            NtHeaders->OptionalHeader.SizeOfImage,
            i,
            Section->VirtualAddress,
            Section->SizeOfRawData,
            Section->Misc.VirtualSize);
    }
}

VOID
NTAPI
MiSetPagingOfDriver(IN PMMPTE PointerPte,
                    IN PMMPTE LastPte)
{
    PVOID ImageBase;
    PETHREAD CurrentThread = PsGetCurrentThread();
    PFN_COUNT PageCount = 0;
    PFN_NUMBER PageFrameIndex;
    PMMPFN Pfn1;
    PAGED_CODE();

    /* The page fault handler is broken and doesn't page back in! */
    DPRINT1("WARNING: MiSetPagingOfDriver() called, but paging is broken! ignoring!\n");
    return;

    /* Get the driver's base address */
    ImageBase = MiPteToAddress(PointerPte);
    ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);

    /* If this is a large page, it's stuck in physical memory */
    if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;

    /* Lock the working set */
    MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);

    /* Loop the PTEs */
    while (PointerPte <= LastPte)
    {
        /* Check for valid PTE */
        if (PointerPte->u.Hard.Valid == 1)
        {
            PageFrameIndex = PFN_FROM_PTE(PointerPte);
            Pfn1 = MiGetPfnEntry(PageFrameIndex);
            ASSERT(Pfn1->u2.ShareCount == 1);

            /* No working sets in ReactOS yet */
            PageCount++;
        }

        ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
        PointerPte++;
    }

    /* Release the working set */
    MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);

    /* Do we have any driver pages? */
    if (PageCount)
    {
        /* Update counters */
        InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
    }
}

VOID
NTAPI
MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
    ULONG_PTR ImageBase;
    PIMAGE_NT_HEADERS NtHeaders;
    ULONG Sections, Alignment, Size;
    PIMAGE_SECTION_HEADER Section;
    PMMPTE PointerPte = NULL, LastPte = NULL;
    if (MmDisablePagingExecutive) return;

    /* Get the driver base address and its NT header */
    ImageBase = (ULONG_PTR)LdrEntry->DllBase;
    NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
    if (!NtHeaders) return;

    /* Get the sections and their alignment */
    Sections = NtHeaders->FileHeader.NumberOfSections;
    Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;

    /* Loop each section */
    Section = IMAGE_FIRST_SECTION(NtHeaders);
    while (Sections)
    {
        /* Find PAGE or .edata */
        if ((*(PULONG)Section->Name == 'EGAP') ||
            (*(PULONG)Section->Name == 'ade.'))
        {
            /* Had we already done some work? */
            if (!PointerPte)
            {
                /* Nope, setup the first PTE address */
                PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
                                                           Section->VirtualAddress));
            }

            /* Compute the size */
            Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);

            /* Find the last PTE that maps this section */
            LastPte = MiAddressToPte(ImageBase +
                                     Section->VirtualAddress +
                                     Alignment + Size - PAGE_SIZE);
        }
        else
        {
            /* Had we found a section before? */
            if (PointerPte)
            {
                /* Mark it as pageable */
                MiSetPagingOfDriver(PointerPte, LastPte);
                PointerPte = NULL;
            }
        }

        /* Keep searching */
        Sections--;
        Section++;
    }

    /* Handle the straggler */
    if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
}

BOOLEAN
NTAPI
MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
{
    PIMAGE_NT_HEADERS NtHeader;
    PAGED_CODE();

    /* Get NT Headers */
    NtHeader = RtlImageNtHeader(BaseAddress);
    if (NtHeader)
    {
        /* Check if this image is only safe for UP while we have 2+ CPUs */
        if ((KeNumberProcessors > 1) &&
            (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
        {
            /* Fail */
            return FALSE;
        }
    }

    /* Otherwise, it's safe */
    return TRUE;
}

NTSTATUS
NTAPI
MmCheckSystemImage(IN HANDLE ImageHandle,
                   IN BOOLEAN PurgeSection)
{
    NTSTATUS Status;
    HANDLE SectionHandle;
    PVOID ViewBase = NULL;
    SIZE_T ViewSize = 0;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_STANDARD_INFORMATION FileStandardInfo;
    KAPC_STATE ApcState;
    PIMAGE_NT_HEADERS NtHeaders;
    OBJECT_ATTRIBUTES ObjectAttributes;
    PAGED_CODE();

    /* Setup the object attributes */
    InitializeObjectAttributes(&ObjectAttributes,
                               NULL,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    /* Create a section for the DLL */
    Status = ZwCreateSection(&SectionHandle,
                             SECTION_MAP_EXECUTE,
                             &ObjectAttributes,
                             NULL,
                             PAGE_EXECUTE,
                             SEC_IMAGE,
                             ImageHandle);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
        return Status;
    }

    /* Make sure we're in the system process */
    KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);

    /* Map it */
    Status = ZwMapViewOfSection(SectionHandle,
                                NtCurrentProcess(),
                                &ViewBase,
                                0,
                                0,
                                NULL,
                                &ViewSize,
                                ViewShare,
                                0,
                                PAGE_EXECUTE);
    if (!NT_SUCCESS(Status))
    {
        /* We failed, close the handle and return */
        DPRINT1("ZwMapViewOfSection failed with status 0x%x\n", Status);
        KeUnstackDetachProcess(&ApcState);
        ZwClose(SectionHandle);
        return Status;
    }

    /* Now query image information */
    Status = ZwQueryInformationFile(ImageHandle,
                                    &IoStatusBlock,
                                    &FileStandardInfo,
                                    sizeof(FileStandardInfo),
                                    FileStandardInformation);
    if (NT_SUCCESS(Status))
    {
        /* First, verify the checksum */
        if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
                                                 ViewSize,
                                                 FileStandardInfo.
                                                 EndOfFile.LowPart))
        {
            /* Set checksum failure */
            Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
            goto Fail;
        }

        /* Make sure it's a real image */
        NtHeaders = RtlImageNtHeader(ViewBase);
        if (!NtHeaders)
        {
            /* Set checksum failure */
            Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
            goto Fail;
        }

        /* Make sure it's for the correct architecture */
        if ((NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_NATIVE) ||
            (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC))
        {
            /* Set protection failure */
            Status = STATUS_INVALID_IMAGE_PROTECT;
            goto Fail;
        }

        /* Check that it's a valid SMP image if we have more then one CPU */
        if (!MmVerifyImageIsOkForMpUse(ViewBase))
        {
            /* Otherwise it's not the right image */
            Status = STATUS_IMAGE_MP_UP_MISMATCH;
        }
    }

    /* Unmap the section, close the handle, and return status */
Fail:
    ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
    KeUnstackDetachProcess(&ApcState);
    ZwClose(SectionHandle);
    return Status;
}

NTSTATUS
NTAPI
MmLoadSystemImage(IN PUNICODE_STRING FileName,
                  IN PUNICODE_STRING NamePrefix OPTIONAL,
                  IN PUNICODE_STRING LoadedName OPTIONAL,
                  IN ULONG Flags,
                  OUT PVOID *ModuleObject,
                  OUT PVOID *ImageBaseAddress)
{
    PVOID ModuleLoadBase = NULL;
    NTSTATUS Status;
    HANDLE FileHandle = NULL;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    PIMAGE_NT_HEADERS NtHeader;
    UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
    PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
    ULONG EntrySize, DriverSize;
    PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
    PCHAR MissingApiName, Buffer;
    PWCHAR MissingDriverName;
    HANDLE SectionHandle;
    ACCESS_MASK DesiredAccess;
    PVOID Section = NULL;
    BOOLEAN LockOwned = FALSE;
    PLIST_ENTRY NextEntry;
    IMAGE_INFO ImageInfo;
    STRING AnsiTemp;
    PAGED_CODE();

    /* Detect session-load */
    if (Flags)
    {
        /* Sanity checks */
        ASSERT(NamePrefix == NULL);
        ASSERT(LoadedName == NULL);

        /* Make sure the process is in session too */
        if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
    }

    /* Allocate a buffer we'll use for names */
    Buffer = ExAllocatePoolWithTag(NonPagedPool,
                                   MAXIMUM_FILENAME_LENGTH,
                                   TAG_LDR_WSTR);
    if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;

    /* Check for a separator */
    if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
    {
        PWCHAR p;
        ULONG BaseLength;

        /* Loop the path until we get to the base name */
        p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
        while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;

        /* Get the length */
        BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
        BaseLength *= sizeof(WCHAR);

        /* Setup the string */
        BaseName.Length = (USHORT)BaseLength;
        BaseName.Buffer = p;
    }
    else
    {
        /* Otherwise, we already have a base name */
        BaseName.Length = FileName->Length;
        BaseName.Buffer = FileName->Buffer;
    }

    /* Setup the maximum length */
    BaseName.MaximumLength = BaseName.Length;

    /* Now compute the base directory */
    BaseDirectory = *FileName;
    BaseDirectory.Length -= BaseName.Length;
    BaseDirectory.MaximumLength = BaseDirectory.Length;

    /* And the prefix, which for now is just the name itself */
    PrefixName = *FileName;

    /* Check if we have a prefix */
    if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");

    /* Check if we already have a name, use it instead */
    if (LoadedName) BaseName = *LoadedName;

    /* Check for loader snap debugging */
    if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
    {
        /* Print out standard string */
        DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
                &PrefixName, &BaseName, Flags ? "in session space" : "");
    }

    /* Acquire the load lock */
LoaderScan:
    ASSERT(LockOwned == FALSE);
    LockOwned = TRUE;
    KeEnterCriticalRegion();
    KeWaitForSingleObject(&MmSystemLoadLock,
                          WrVirtualMemory,
                          KernelMode,
                          FALSE,
                          NULL);

    /* Scan the module list */
    NextEntry = PsLoadedModuleList.Flink;
    while (NextEntry != &PsLoadedModuleList)
    {
        /* Get the entry and compare the names */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);
        if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
        {
            /* Found it, break out */
            break;
        }

        /* Keep scanning */
        NextEntry = NextEntry->Flink;
    }

    /* Check if we found the image */
    if (NextEntry != &PsLoadedModuleList)
    {
        /* Check if we had already mapped a section */
        if (Section)
        {
            /* Dereference and clear */
            ObDereferenceObject(Section);
            Section = NULL;
        }

        /* Check if this was supposed to be a session load */
        if (!Flags)
        {
            /* It wasn't, so just return the data */
            *ModuleObject = LdrEntry;
            *ImageBaseAddress = LdrEntry->DllBase;
            Status = STATUS_IMAGE_ALREADY_LOADED;
        }
        else
        {
            /* We don't support session loading yet */
            UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
            Status = STATUS_NOT_IMPLEMENTED;
        }

        /* Do cleanup */
        goto Quickie;
    }
    else if (!Section)
    {
        /* It wasn't loaded, and we didn't have a previous attempt */
        KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
        KeLeaveCriticalRegion();
        LockOwned = FALSE;

        /* Check if KD is enabled */
        if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
        {
            /* FIXME: Attempt to get image from KD */
        }

        /* We don't have a valid entry */
        LdrEntry = NULL;

        /* Setup image attributes */
        InitializeObjectAttributes(&ObjectAttributes,
                                   FileName,
                                   OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                   NULL,
                                   NULL);

        /* Open the image */
        Status = ZwOpenFile(&FileHandle,
                            FILE_EXECUTE,
                            &ObjectAttributes,
                            &IoStatusBlock,
                            FILE_SHARE_READ | FILE_SHARE_DELETE,
                            0);
        if (!NT_SUCCESS(Status))
        {
            DPRINT1("ZwOpenFile failed for '%wZ' with status 0x%x\n",
                    FileName, Status);
            goto Quickie;
        }

        /* Validate it */
        Status = MmCheckSystemImage(FileHandle, FALSE);
        if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
            (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
            (Status == STATUS_INVALID_IMAGE_PROTECT))
        {
            /* Fail loading */
            goto Quickie;
        }

        /* Check if this is a session-load */
        if (Flags)
        {
            /* Then we only need read and execute */
            DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
        }
        else
        {
            /* Otherwise, we can allow write access */
            DesiredAccess = SECTION_ALL_ACCESS;
        }

        /* Initialize the attributes for the section */
        InitializeObjectAttributes(&ObjectAttributes,
                                   NULL,
                                   OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                   NULL,
                                   NULL);

        /* Create the section */
        Status = ZwCreateSection(&SectionHandle,
                                 DesiredAccess,
                                 &ObjectAttributes,
                                 NULL,
                                 PAGE_EXECUTE,
                                 SEC_IMAGE,
                                 FileHandle);
        if (!NT_SUCCESS(Status))
        {
            DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
            goto Quickie;
        }

        /* Now get the section pointer */
        Status = ObReferenceObjectByHandle(SectionHandle,
                                           SECTION_MAP_EXECUTE,
                                           MmSectionObjectType,
                                           KernelMode,
                                           &Section,
                                           NULL);
        ZwClose(SectionHandle);
        if (!NT_SUCCESS(Status)) goto Quickie;

        /* Check if this was supposed to be a session-load */
        if (Flags)
        {
            /* We don't support session loading yet */
            UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
            goto Quickie;
        }

        /* Check the loader list again, we should end up in the path below */
        goto LoaderScan;
    }
    else
    {
        /* We don't have a valid entry */
        LdrEntry = NULL;
    }

    /* Load the image */
    Status = MiLoadImageSection(&Section,
                                &ModuleLoadBase,
                                FileName,
                                FALSE,
                                NULL);
    ASSERT(Status != STATUS_ALREADY_COMMITTED);

    /* Get the size of the driver */
    DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageInformation.ImageFileSize;

    /* Make sure we're not being loaded into session space */
    if (!Flags)
    {
        /* Check for success */
        if (NT_SUCCESS(Status))
        {
            /* Support large pages for drivers */
            MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
                                 &ModuleLoadBase,
                                 &BaseName,
                                 TRUE);
        }

        /* Dereference the section */
        ObDereferenceObject(Section);
        Section = NULL;
    }

    /* Check for failure of the load earlier */
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("MiLoadImageSection failed with status 0x%x\n", Status);
        goto Quickie;
    }

    /* Relocate the driver */
    Status = LdrRelocateImageWithBias(ModuleLoadBase,
                                      0,
                                      "SYSLDR",
                                      STATUS_SUCCESS,
                                      STATUS_CONFLICTING_ADDRESSES,
                                      STATUS_INVALID_IMAGE_FORMAT);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("LdrRelocateImageWithBias failed with status 0x%x\n", Status);
        goto Quickie;
    }

    /* Get the NT Header */
    NtHeader = RtlImageNtHeader(ModuleLoadBase);

    /* Calculate the size we'll need for the entry and allocate it */
    EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
                BaseName.Length +
                sizeof(UNICODE_NULL);

    /* Allocate the entry */
    LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
    if (!LdrEntry)
    {
        /* Fail */
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Quickie;
    }

    /* Setup the entry */
    LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
    LdrEntry->LoadCount = 1;
    LdrEntry->LoadedImports = LoadedImports;
    LdrEntry->PatchInformation = NULL;

    /* Check the version */
    if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
        (NtHeader->OptionalHeader.MajorImageVersion >= 5))
    {
        /* Mark this image as a native image */
        LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
    }

    /* Setup the rest of the entry */
    LdrEntry->DllBase = ModuleLoadBase;
    LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
                                   NtHeader->OptionalHeader.AddressOfEntryPoint);
    LdrEntry->SizeOfImage = DriverSize;
    LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
    LdrEntry->SectionPointer = Section;

    /* Now write the DLL name */
    LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
    LdrEntry->BaseDllName.Length = BaseName.Length;
    LdrEntry->BaseDllName.MaximumLength = BaseName.Length;

    /* Copy and null-terminate it */
    RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
                  BaseName.Buffer,
                  BaseName.Length);
    LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;

    /* Now allocate the full name */
    LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
                                                         PrefixName.Length +
                                                         sizeof(UNICODE_NULL),
                                                         TAG_LDR_WSTR);
    if (!LdrEntry->FullDllName.Buffer)
    {
        /* Don't fail, just set it to zero */
        LdrEntry->FullDllName.Length = 0;
        LdrEntry->FullDllName.MaximumLength = 0;
    }
    else
    {
        /* Set it up */
        LdrEntry->FullDllName.Length = PrefixName.Length;
        LdrEntry->FullDllName.MaximumLength = PrefixName.Length;

        /* Copy and null-terminate */
        RtlCopyMemory(LdrEntry->FullDllName.Buffer,
                      PrefixName.Buffer,
                      PrefixName.Length);
        LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
    }

    /* Add the entry */
    MiProcessLoaderEntry(LdrEntry, TRUE);

    /* Resolve imports */
    MissingApiName = Buffer;
    MissingDriverName = NULL;
    Status = MiResolveImageReferences(ModuleLoadBase,
                                      &BaseDirectory,
                                      NULL,
                                      &MissingApiName,
                                      &MissingDriverName,
                                      &LoadedImports);
    if (!NT_SUCCESS(Status))
    {
        BOOLEAN NeedToFreeString = FALSE;

        /* If the lowest bit is set to 1, this is a hint that we need to free */
        if (*(ULONG_PTR*)&MissingDriverName & 1)
        {
            NeedToFreeString = TRUE;
            *(ULONG_PTR*)&MissingDriverName &= ~1;
        }

        DPRINT1("MiResolveImageReferences failed with status 0x%x\n", Status);
        DPRINT1(" Missing driver '%ls', missing API '%s'\n",
                MissingDriverName, MissingApiName);

        if (NeedToFreeString)
        {
            ExFreePoolWithTag(MissingDriverName, TAG_LDR_WSTR);
        }

        /* Fail */
        MiProcessLoaderEntry(LdrEntry, FALSE);

        /* Check if we need to free the name */
        if (LdrEntry->FullDllName.Buffer)
        {
            /* Free it */
            ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
        }

        /* Free the entry itself */
        ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
        LdrEntry = NULL;
        goto Quickie;
    }

    /* Update the loader entry */
    LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
                        LDRP_ENTRY_PROCESSED |
                        LDRP_MM_LOADED);
    LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
    LdrEntry->LoadedImports = LoadedImports;

    /* FIXME: Call driver verifier's loader function */

    /* Write-protect the system image */
    MiWriteProtectSystemImage(LdrEntry->DllBase);

    /* Check if notifications are enabled */
    if (PsImageNotifyEnabled)
    {
        /* Fill out the notification data */
        ImageInfo.Properties = 0;
        ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
        ImageInfo.SystemModeImage = TRUE;
        ImageInfo.ImageSize = LdrEntry->SizeOfImage;
        ImageInfo.ImageBase = LdrEntry->DllBase;
        ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;

        /* Send the notification */
        PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
    }

#ifdef __ROS_ROSSYM__
    /* MiCacheImageSymbols doesn't detect rossym */
    if (TRUE)
#else
    /* Check if there's symbols */
    if (MiCacheImageSymbols(LdrEntry->DllBase))
#endif
    {
        /* Check if the system root is present */
        if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
            !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
        {
            /* Add the system root */
            UnicodeTemp = PrefixName;
            UnicodeTemp.Buffer += 11;
            UnicodeTemp.Length -= (11 * sizeof(WCHAR));
            sprintf_nt(Buffer,
                       "%ws%wZ",
                       &SharedUserData->NtSystemRoot[2],
                       &UnicodeTemp);
        }
        else
        {
            /* Build the name */
            sprintf_nt(Buffer, "%wZ", &BaseName);
        }

        /* Setup the ansi string */
        RtlInitString(&AnsiTemp, Buffer);

        /* Notify the debugger */
        DbgLoadImageSymbols(&AnsiTemp,
                            LdrEntry->DllBase,
                            (ULONG_PTR)PsGetCurrentProcessId());
        LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
    }

    /* Page the driver */
    ASSERT(Section == NULL);
    MiEnablePagingOfDriver(LdrEntry);

    /* Return pointers */
    *ModuleObject = LdrEntry;
    *ImageBaseAddress = LdrEntry->DllBase;

Quickie:
    /* Check if we have the lock acquired */
    if (LockOwned)
    {
        /* Release the lock */
        KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
        KeLeaveCriticalRegion();
        LockOwned = FALSE;
    }

    /* If we have a file handle, close it */
    if (FileHandle) ZwClose(FileHandle);

    /* Check if we had a prefix (not supported yet - PrefixName == *FileName now) */
    /* if (NamePrefix) ExFreePool(PrefixName.Buffer); */

    /* Free the name buffer and return status */
    ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
    return Status;
}

PLDR_DATA_TABLE_ENTRY
NTAPI
MiLookupDataTableEntry(IN PVOID Address)
{
    PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
    PLIST_ENTRY NextEntry;
    PAGED_CODE();

    /* Loop entries */
    NextEntry = PsLoadedModuleList.Flink;
    do
    {
        /* Get the loader entry */
        LdrEntry =  CONTAINING_RECORD(NextEntry,
                                      LDR_DATA_TABLE_ENTRY,
                                      InLoadOrderLinks);

        /* Check if the address matches */
        if ((Address >= LdrEntry->DllBase) &&
            (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
                               LdrEntry->SizeOfImage)))
        {
            /* Found a match */
            FoundEntry = LdrEntry;
            break;
        }

        /* Move on */
        NextEntry = NextEntry->Flink;
    } while(NextEntry != &PsLoadedModuleList);

    /* Return the entry */
    return FoundEntry;
}

/* PUBLIC FUNCTIONS ***********************************************************/

/*
 * @implemented
 */
PVOID
NTAPI
MmPageEntireDriver(IN PVOID AddressWithinSection)
{
    PMMPTE StartPte, EndPte;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    PAGED_CODE();

    /* Get the loader entry */
    LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
    if (!LdrEntry) return NULL;

    /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
    if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
    {
        /* Don't do anything, just return the base address */
        return LdrEntry->DllBase;
    }

    /* Wait for active DPCs to finish before we page out the driver */
    KeFlushQueuedDpcs();

    /* Get the PTE range for the whole driver image */
    StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
    EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);

    /* Enable paging for the PTE range */
    ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
    MiSetPagingOfDriver(StartPte, EndPte);

    /* Return the base address */
    return LdrEntry->DllBase;
}

/*
 * @unimplemented
 */
VOID
NTAPI
MmResetDriverPaging(IN PVOID AddressWithinSection)
{
    UNIMPLEMENTED;
}

/*
 * @implemented
 */
PVOID
NTAPI
MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
{
    PVOID ProcAddress = NULL;
    ANSI_STRING AnsiRoutineName;
    NTSTATUS Status;
    PLIST_ENTRY NextEntry;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    BOOLEAN Found = FALSE;
    UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
    UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
    ULONG Modules = 0;

    /* Convert routine to ansi name */
    Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
                                          SystemRoutineName,
                                          TRUE);
    if (!NT_SUCCESS(Status)) return NULL;

    /* Lock the list */
    KeEnterCriticalRegion();
    ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);

    /* Loop the loaded module list */
    NextEntry = PsLoadedModuleList.Flink;
    while (NextEntry != &PsLoadedModuleList)
    {
        /* Get the entry */
        LdrEntry = CONTAINING_RECORD(NextEntry,
                                     LDR_DATA_TABLE_ENTRY,
                                     InLoadOrderLinks);

        /* Check if it's the kernel or HAL */
        if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
        {
            /* Found it */
            Found = TRUE;
            Modules++;
        }
        else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
        {
            /* Found it */
            Found = TRUE;
            Modules++;
        }

        /* Check if we found a valid binary */
        if (Found)
        {
            /* Find the procedure name */
            ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
                                                      &AnsiRoutineName);

            /* Break out if we found it or if we already tried both modules */
            if (ProcAddress) break;
            if (Modules == 2) break;
        }

        /* Keep looping */
        NextEntry = NextEntry->Flink;
    }

    /* Release the lock */
    ExReleaseResourceLite(&PsLoadedModuleResource);
    KeLeaveCriticalRegion();

    /* Free the string and return */
    RtlFreeAnsiString(&AnsiRoutineName);
    return ProcAddress;
}

/* EOF */