xref: /reactos/ntoskrnl/mm/ARM3/sysldr.c (revision 7b1049c8)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/sysldr.c
5  * PURPOSE:         Contains the Kernel Loader (SYSLDR) for loading PE files.
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  ReactOS Portable Systems Group
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
18 
19 static
20 inline
21 VOID
22 sprintf_nt(IN PCHAR Buffer,
23            IN PCHAR Format,
24            IN ...)
25 {
26     va_list ap;
27     va_start(ap, Format);
28     vsprintf(Buffer, Format, ap);
29     va_end(ap);
30 }
31 
32 /* GLOBALS ********************************************************************/
33 
34 LIST_ENTRY PsLoadedModuleList;
35 LIST_ENTRY MmLoadedUserImageList;
36 KSPIN_LOCK PsLoadedModuleSpinLock;
37 ERESOURCE PsLoadedModuleResource;
38 ULONG_PTR PsNtosImageBase;
39 KMUTANT MmSystemLoadLock;
40 
41 PFN_NUMBER MmTotalSystemDriverPages;
42 
43 PVOID MmUnloadedDrivers;
44 PVOID MmLastUnloadedDrivers;
45 
46 BOOLEAN MmMakeLowMemory;
47 BOOLEAN MmEnforceWriteProtection = TRUE;
48 
49 PMMPTE MiKernelResourceStartPte, MiKernelResourceEndPte;
50 ULONG_PTR ExPoolCodeStart, ExPoolCodeEnd, MmPoolCodeStart, MmPoolCodeEnd;
51 ULONG_PTR MmPteCodeStart, MmPteCodeEnd;
52 
53 /* FUNCTIONS ******************************************************************/
54 
55 PVOID
56 NTAPI
57 MiCacheImageSymbols(IN PVOID BaseAddress)
58 {
59     ULONG DebugSize;
60     PVOID DebugDirectory = NULL;
61     PAGED_CODE();
62 
63     /* Make sure it's safe to access the image */
64     _SEH2_TRY
65     {
66         /* Get the debug directory */
67         DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
68                                                       TRUE,
69                                                       IMAGE_DIRECTORY_ENTRY_DEBUG,
70                                                       &DebugSize);
71     }
72     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
73     {
74         /* Nothing */
75     }
76     _SEH2_END;
77 
78     /* Return the directory */
79     return DebugDirectory;
80 }
81 
82 NTSTATUS
83 NTAPI
84 MiLoadImageSection(_Inout_ PSECTION *SectionPtr,
85                    _Out_ PVOID *ImageBase,
86                    _In_ PUNICODE_STRING FileName,
87                    _In_ BOOLEAN SessionLoad,
88                    _In_ PLDR_DATA_TABLE_ENTRY LdrEntry)
89 {
90     PSECTION Section = *SectionPtr;
91     NTSTATUS Status;
92     PEPROCESS Process;
93     PVOID Base = NULL;
94     SIZE_T ViewSize = 0;
95     KAPC_STATE ApcState;
96     LARGE_INTEGER SectionOffset = {{0, 0}};
97     BOOLEAN LoadSymbols = FALSE;
98     PFN_COUNT PteCount;
99     PMMPTE PointerPte, LastPte;
100     PVOID DriverBase;
101     MMPTE TempPte;
102     KIRQL OldIrql;
103     PFN_NUMBER PageFrameIndex;
104     PAGED_CODE();
105 
106     /* Detect session load */
107     if (SessionLoad)
108     {
109         /* Fail */
110         UNIMPLEMENTED_DBGBREAK("Session loading not yet supported!\n");
111         return STATUS_NOT_IMPLEMENTED;
112     }
113 
114     /* Not session load, shouldn't have an entry */
115     ASSERT(LdrEntry == NULL);
116 
117     /* Attach to the system process */
118     KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
119 
120     /* Check if we need to load symbols */
121     if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
122     {
123         /* Yes we do */
124         LoadSymbols = TRUE;
125         NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
126     }
127 
128     /* Map the driver */
129     Process = PsGetCurrentProcess();
130     Status = MmMapViewOfSection(Section,
131                                 Process,
132                                 &Base,
133                                 0,
134                                 0,
135                                 &SectionOffset,
136                                 &ViewSize,
137                                 ViewUnmap,
138                                 0,
139                                 PAGE_EXECUTE);
140 
141     /* Re-enable the flag */
142     if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
143 
144     /* Check if we failed with distinguished status code */
145     if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
146     {
147         /* Change it to something more generic */
148         Status = STATUS_INVALID_IMAGE_FORMAT;
149     }
150 
151     /* Now check if we failed */
152     if (!NT_SUCCESS(Status))
153     {
154         /* Detach and return */
155         DPRINT1("MmMapViewOfSection failed with status 0x%x\n", Status);
156         KeUnstackDetachProcess(&ApcState);
157         return Status;
158     }
159 
160     /* Reserve system PTEs needed */
161     PteCount = ROUND_TO_PAGES(((PMM_IMAGE_SECTION_OBJECT)Section->Segment)->ImageInformation.ImageFileSize) >> PAGE_SHIFT;
162     PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
163     if (!PointerPte)
164     {
165         DPRINT1("MiReserveSystemPtes failed\n");
166         KeUnstackDetachProcess(&ApcState);
167         return STATUS_INSUFFICIENT_RESOURCES;
168     }
169 
170     /* New driver base */
171     LastPte = PointerPte + PteCount;
172     DriverBase = MiPteToAddress(PointerPte);
173 
174     /* The driver is here */
175     *ImageBase = DriverBase;
176     DPRINT1("Loading: %wZ at %p with %lx pages\n", FileName, DriverBase, PteCount);
177 
178     /* Lock the PFN database */
179     OldIrql = MiAcquirePfnLock();
180 
181     /* Loop the new driver PTEs */
182     TempPte = ValidKernelPte;
183     while (PointerPte < LastPte)
184     {
185         /* Make sure the PTE is not valid for whatever reason */
186         ASSERT(PointerPte->u.Hard.Valid == 0);
187 
188         /* Some debug stuff */
189         MI_SET_USAGE(MI_USAGE_DRIVER_PAGE);
190 #if MI_TRACE_PFNS
191         if (FileName->Buffer)
192         {
193             PWCHAR pos = NULL;
194             ULONG len = 0;
195             pos = wcsrchr(FileName->Buffer, '\\');
196             len = wcslen(pos) * sizeof(WCHAR);
197             if (pos) snprintf(MI_PFN_CURRENT_PROCESS_NAME, min(16, len), "%S", pos);
198         }
199 #endif
200 
201         /* Grab a page */
202         PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
203 
204         /* Initialize its PFN entry */
205         MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
206 
207         /* Write the PTE */
208         TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
209         MI_WRITE_VALID_PTE(PointerPte, TempPte);
210 
211         /* Move on */
212         PointerPte++;
213     }
214 
215     /* Release the PFN lock */
216     MiReleasePfnLock(OldIrql);
217 
218     /* Copy the image */
219     RtlCopyMemory(DriverBase, Base, PteCount << PAGE_SHIFT);
220 
221     /* Now unmap the view */
222     Status = MmUnmapViewOfSection(Process, Base);
223     ASSERT(NT_SUCCESS(Status));
224 
225     /* Detach and return status */
226     KeUnstackDetachProcess(&ApcState);
227     return Status;
228 }
229 
230 PVOID
231 NTAPI
232 MiLocateExportName(IN PVOID DllBase,
233                    IN PCHAR ExportName)
234 {
235     PULONG NameTable;
236     PUSHORT OrdinalTable;
237     PIMAGE_EXPORT_DIRECTORY ExportDirectory;
238     LONG Low = 0, Mid = 0, High, Ret;
239     USHORT Ordinal;
240     PVOID Function;
241     ULONG ExportSize;
242     PULONG ExportTable;
243     PAGED_CODE();
244 
245     /* Get the export directory */
246     ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
247                                                    TRUE,
248                                                    IMAGE_DIRECTORY_ENTRY_EXPORT,
249                                                    &ExportSize);
250     if (!ExportDirectory) return NULL;
251 
252     /* Setup name tables */
253     NameTable = (PULONG)((ULONG_PTR)DllBase +
254                          ExportDirectory->AddressOfNames);
255     OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
256                              ExportDirectory->AddressOfNameOrdinals);
257 
258     /* Do a binary search */
259     High = ExportDirectory->NumberOfNames - 1;
260     while (High >= Low)
261     {
262         /* Get new middle value */
263         Mid = (Low + High) >> 1;
264 
265         /* Compare name */
266         Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
267         if (Ret < 0)
268         {
269             /* Update high */
270             High = Mid - 1;
271         }
272         else if (Ret > 0)
273         {
274             /* Update low */
275             Low = Mid + 1;
276         }
277         else
278         {
279             /* We got it */
280             break;
281         }
282     }
283 
284     /* Check if we couldn't find it */
285     if (High < Low) return NULL;
286 
287     /* Otherwise, this is the ordinal */
288     Ordinal = OrdinalTable[Mid];
289 
290     /* Resolve the address and write it */
291     ExportTable = (PULONG)((ULONG_PTR)DllBase +
292                            ExportDirectory->AddressOfFunctions);
293     Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
294 
295     /* Check if the function is actually a forwarder */
296     if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
297         ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
298     {
299         /* It is, fail */
300         return NULL;
301     }
302 
303     /* We found it */
304     return Function;
305 }
306 
307 NTSTATUS
308 NTAPI
309 MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
310                     IN PLIST_ENTRY ListHead)
311 {
312     UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
313         L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
314     PMM_DLL_INITIALIZE DllInit;
315     UNICODE_STRING RegPath, ImportName;
316     NTSTATUS Status;
317 
318     /* Try to see if the image exports a DllInitialize routine */
319     DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
320                                                      "DllInitialize");
321     if (!DllInit) return STATUS_SUCCESS;
322 
323     /*
324      * Do a temporary copy of BaseDllName called ImportName
325      * because we'll alter the length of the string.
326      */
327     ImportName.Length = LdrEntry->BaseDllName.Length;
328     ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
329     ImportName.Buffer = LdrEntry->BaseDllName.Buffer;
330 
331     /* Obtain the path to this dll's service in the registry */
332     RegPath.MaximumLength = ServicesKeyName.Length +
333         ImportName.Length + sizeof(UNICODE_NULL);
334     RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
335                                            RegPath.MaximumLength,
336                                            TAG_LDR_WSTR);
337 
338     /* Check if this allocation was unsuccessful */
339     if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
340 
341     /* Build and append the service name itself */
342     RegPath.Length = ServicesKeyName.Length;
343     RtlCopyMemory(RegPath.Buffer,
344                   ServicesKeyName.Buffer,
345                   ServicesKeyName.Length);
346 
347     /* Check if there is a dot in the filename */
348     if (wcschr(ImportName.Buffer, L'.'))
349     {
350         /* Remove the extension */
351         ImportName.Length = (USHORT)(wcschr(ImportName.Buffer, L'.') -
352             ImportName.Buffer) * sizeof(WCHAR);
353     }
354 
355     /* Append service name (the basename without extension) */
356     RtlAppendUnicodeStringToString(&RegPath, &ImportName);
357 
358     /* Now call the DllInit func */
359     DPRINT("Calling DllInit(%wZ)\n", &RegPath);
360     Status = DllInit(&RegPath);
361 
362     /* Clean up */
363     ExFreePoolWithTag(RegPath.Buffer, TAG_LDR_WSTR);
364 
365     /* Return status value which DllInitialize returned */
366     return Status;
367 }
368 
369 BOOLEAN
370 NTAPI
371 MiCallDllUnloadAndUnloadDll(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
372 {
373     NTSTATUS Status;
374     PMM_DLL_UNLOAD Func;
375     PAGED_CODE();
376 
377     /* Get the unload routine */
378     Func = (PMM_DLL_UNLOAD)MiLocateExportName(LdrEntry->DllBase, "DllUnload");
379     if (!Func) return FALSE;
380 
381     /* Call it and check for success */
382     Status = Func();
383     if (!NT_SUCCESS(Status)) return FALSE;
384 
385     /* Lie about the load count so we can unload the image */
386     ASSERT(LdrEntry->LoadCount == 0);
387     LdrEntry->LoadCount = 1;
388 
389     /* Unload it and return true */
390     MmUnloadSystemImage(LdrEntry);
391     return TRUE;
392 }
393 
394 NTSTATUS
395 NTAPI
396 MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
397 {
398     SIZE_T i;
399     LOAD_IMPORTS SingleEntry;
400     PLDR_DATA_TABLE_ENTRY LdrEntry;
401     PVOID CurrentImports;
402     PAGED_CODE();
403 
404     /* Check if there's no imports or if we're a boot driver */
405     if ((ImportList == MM_SYSLDR_NO_IMPORTS) ||
406         (ImportList == MM_SYSLDR_BOOT_LOADED) ||
407         (ImportList->Count == 0))
408     {
409         /* Then there's nothing to do */
410         return STATUS_SUCCESS;
411     }
412 
413     /* Check for single-entry */
414     if ((ULONG_PTR)ImportList & MM_SYSLDR_SINGLE_ENTRY)
415     {
416         /* Set it up */
417         SingleEntry.Count = 1;
418         SingleEntry.Entry[0] = (PVOID)((ULONG_PTR)ImportList &~ MM_SYSLDR_SINGLE_ENTRY);
419 
420         /* Use this as the import list */
421         ImportList = &SingleEntry;
422     }
423 
424     /* Loop the import list */
425     for (i = 0; (i < ImportList->Count) && (ImportList->Entry[i]); i++)
426     {
427         /* Get the entry */
428         LdrEntry = ImportList->Entry[i];
429         DPRINT1("%wZ <%wZ>\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName);
430 
431         /* Skip boot loaded images */
432         if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) continue;
433 
434         /* Dereference the entry */
435         ASSERT(LdrEntry->LoadCount >= 1);
436         if (!--LdrEntry->LoadCount)
437         {
438             /* Save the import data in case unload fails */
439             CurrentImports = LdrEntry->LoadedImports;
440 
441             /* This is the last entry */
442             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
443             if (MiCallDllUnloadAndUnloadDll(LdrEntry))
444             {
445                 /* Unloading worked, parse this DLL's imports too */
446                 MiDereferenceImports(CurrentImports);
447 
448                 /* Check if we had valid imports */
449                 if ((CurrentImports != MM_SYSLDR_BOOT_LOADED) &&
450                     (CurrentImports != MM_SYSLDR_NO_IMPORTS) &&
451                     !((ULONG_PTR)CurrentImports & MM_SYSLDR_SINGLE_ENTRY))
452                 {
453                     /* Free them */
454                     ExFreePoolWithTag(CurrentImports, TAG_LDR_IMPORTS);
455                 }
456             }
457             else
458             {
459                 /* Unload failed, restore imports */
460                 LdrEntry->LoadedImports = CurrentImports;
461             }
462         }
463     }
464 
465     /* Done */
466     return STATUS_SUCCESS;
467 }
468 
469 VOID
470 NTAPI
471 MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
472 {
473     PAGED_CODE();
474 
475     /* Check if there's no imports or we're a boot driver or only one entry */
476     if ((LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) ||
477         (LdrEntry->LoadedImports == MM_SYSLDR_NO_IMPORTS) ||
478         ((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
479     {
480         /* Nothing to do */
481         return;
482     }
483 
484     /* Otherwise, free the import list */
485     ExFreePoolWithTag(LdrEntry->LoadedImports, TAG_LDR_IMPORTS);
486     LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
487 }
488 
489 PVOID
490 NTAPI
491 MiFindExportedRoutineByName(IN PVOID DllBase,
492                             IN PANSI_STRING ExportName)
493 {
494     PULONG NameTable;
495     PUSHORT OrdinalTable;
496     PIMAGE_EXPORT_DIRECTORY ExportDirectory;
497     LONG Low = 0, Mid = 0, High, Ret;
498     USHORT Ordinal;
499     PVOID Function;
500     ULONG ExportSize;
501     PULONG ExportTable;
502     PAGED_CODE();
503 
504     /* Get the export directory */
505     ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
506                                                    TRUE,
507                                                    IMAGE_DIRECTORY_ENTRY_EXPORT,
508                                                    &ExportSize);
509     if (!ExportDirectory) return NULL;
510 
511     /* Setup name tables */
512     NameTable = (PULONG)((ULONG_PTR)DllBase +
513                          ExportDirectory->AddressOfNames);
514     OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
515                              ExportDirectory->AddressOfNameOrdinals);
516 
517     /* Do a binary search */
518     High = ExportDirectory->NumberOfNames - 1;
519     while (High >= Low)
520     {
521         /* Get new middle value */
522         Mid = (Low + High) >> 1;
523 
524         /* Compare name */
525         Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
526         if (Ret < 0)
527         {
528             /* Update high */
529             High = Mid - 1;
530         }
531         else if (Ret > 0)
532         {
533             /* Update low */
534             Low = Mid + 1;
535         }
536         else
537         {
538             /* We got it */
539             break;
540         }
541     }
542 
543     /* Check if we couldn't find it */
544     if (High < Low) return NULL;
545 
546     /* Otherwise, this is the ordinal */
547     Ordinal = OrdinalTable[Mid];
548 
549     /* Validate the ordinal */
550     if (Ordinal >= ExportDirectory->NumberOfFunctions) return NULL;
551 
552     /* Resolve the address and write it */
553     ExportTable = (PULONG)((ULONG_PTR)DllBase +
554                            ExportDirectory->AddressOfFunctions);
555     Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
556 
557     /* We found it! */
558     ASSERT((Function < (PVOID)ExportDirectory) ||
559            (Function > (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
560 
561     return Function;
562 }
563 
564 VOID
565 NTAPI
566 MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
567                      IN BOOLEAN Insert)
568 {
569     KIRQL OldIrql;
570 
571     /* Acquire module list lock */
572     KeEnterCriticalRegion();
573     ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
574 
575     /* Acquire the spinlock too as we will insert or remove the entry */
576     OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);
577 
578     /* Insert or remove from the list */
579     if (Insert)
580         InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks);
581     else
582         RemoveEntryList(&LdrEntry->InLoadOrderLinks);
583 
584     /* Release locks */
585     KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
586     ExReleaseResourceLite(&PsLoadedModuleResource);
587     KeLeaveCriticalRegion();
588 }
589 
590 CODE_SEG("INIT")
591 VOID
592 NTAPI
593 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
594                IN PVOID OldBase,
595                IN PVOID NewBase,
596                IN ULONG Size)
597 {
598     ULONG_PTR OldBaseTop, Delta;
599     PLDR_DATA_TABLE_ENTRY LdrEntry;
600     PLIST_ENTRY NextEntry;
601     ULONG ImportSize;
602     //
603     // FIXME: MINGW-W64 must fix LD to generate drivers that Windows can load,
604     // since a real version of Windows would fail at this point, but they seem
605     // busy implementing features such as "HotPatch" support in GCC 4.6 instead,
606     // a feature which isn't even used by Windows. Priorities, priorities...
607     // Please note that Microsoft WDK EULA and license prohibits using
608     // the information contained within it for the generation of "non-Windows"
609     // drivers, which is precisely what LD will generate, since an LD driver
610     // will not load on Windows.
611     //
612 #ifdef _WORKING_LINKER_
613     ULONG i;
614 #endif
615     PULONG_PTR ImageThunk;
616     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
617 
618     /* Calculate the top and delta */
619     OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
620     Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
621 
622     /* Loop the loader block */
623     for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
624          NextEntry != &LoaderBlock->LoadOrderListHead;
625          NextEntry = NextEntry->Flink)
626     {
627         /* Get the loader entry */
628         LdrEntry = CONTAINING_RECORD(NextEntry,
629                                      LDR_DATA_TABLE_ENTRY,
630                                      InLoadOrderLinks);
631 #ifdef _WORKING_LINKER_
632         /* Get the IAT */
633         ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
634                                                   TRUE,
635                                                   IMAGE_DIRECTORY_ENTRY_IAT,
636                                                   &ImportSize);
637         if (!ImageThunk) continue;
638 
639         /* Make sure we have an IAT */
640         DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
641         for (i = 0; i < ImportSize; i++, ImageThunk++)
642         {
643             /* Check if it's within this module */
644             if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
645             {
646                 /* Relocate it */
647                 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
648                         ImageThunk, *ImageThunk, *ImageThunk + Delta);
649                 *ImageThunk += Delta;
650             }
651         }
652 #else
653         /* Get the import table */
654         ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
655                                                         TRUE,
656                                                         IMAGE_DIRECTORY_ENTRY_IMPORT,
657                                                         &ImportSize);
658         if (!ImportDescriptor) continue;
659 
660         /* Make sure we have an IAT */
661         DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
662         while ((ImportDescriptor->Name) &&
663                (ImportDescriptor->OriginalFirstThunk))
664         {
665             /* Get the image thunk */
666             ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
667                                  ImportDescriptor->FirstThunk);
668             while (*ImageThunk)
669             {
670                 /* Check if it's within this module */
671                 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
672                 {
673                     /* Relocate it */
674                     DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
675                             ImageThunk, *ImageThunk, *ImageThunk + Delta);
676                     *ImageThunk += Delta;
677                 }
678 
679                 /* Go to the next thunk */
680                 ImageThunk++;
681             }
682 
683             /* Go to the next import */
684             ImportDescriptor++;
685         }
686 #endif
687     }
688 }
689 
690 NTSTATUS
691 NTAPI
692 MiSnapThunk(IN PVOID DllBase,
693             IN PVOID ImageBase,
694             IN PIMAGE_THUNK_DATA Name,
695             IN PIMAGE_THUNK_DATA Address,
696             IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
697             IN ULONG ExportSize,
698             IN BOOLEAN SnapForwarder,
699             OUT PCHAR *MissingApi)
700 {
701     BOOLEAN IsOrdinal;
702     USHORT Ordinal;
703     PULONG NameTable;
704     PUSHORT OrdinalTable;
705     PIMAGE_IMPORT_BY_NAME NameImport;
706     USHORT Hint;
707     ULONG Low = 0, Mid = 0, High;
708     LONG Ret;
709     NTSTATUS Status;
710     PCHAR MissingForwarder;
711     CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
712     PULONG ExportTable;
713     ANSI_STRING DllName;
714     UNICODE_STRING ForwarderName;
715     PLIST_ENTRY NextEntry;
716     PLDR_DATA_TABLE_ENTRY LdrEntry;
717     ULONG ForwardExportSize;
718     PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
719     PIMAGE_IMPORT_BY_NAME ForwardName;
720     SIZE_T ForwardLength;
721     IMAGE_THUNK_DATA ForwardThunk;
722     PAGED_CODE();
723 
724     /* Check if this is an ordinal */
725     IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
726     if ((IsOrdinal) && !(SnapForwarder))
727     {
728         /* Get the ordinal number and set it as missing */
729         Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
730                            ExportDirectory->Base);
731         *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
732     }
733     else
734     {
735         /* Get the VA if we don't have to snap */
736         if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
737         NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
738 
739         /* Copy the procedure name */
740         RtlStringCbCopyA(*MissingApi,
741                          MAXIMUM_FILENAME_LENGTH,
742                          (PCHAR)&NameImport->Name[0]);
743 
744         /* Setup name tables */
745         DPRINT("Import name: %s\n", NameImport->Name);
746         NameTable = (PULONG)((ULONG_PTR)DllBase +
747                              ExportDirectory->AddressOfNames);
748         OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
749                                  ExportDirectory->AddressOfNameOrdinals);
750 
751         /* Get the hint and check if it's valid */
752         Hint = NameImport->Hint;
753         if ((Hint < ExportDirectory->NumberOfNames) &&
754             !(strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
755         {
756             /* We have a match, get the ordinal number from here */
757             Ordinal = OrdinalTable[Hint];
758         }
759         else
760         {
761             /* Do a binary search */
762             High = ExportDirectory->NumberOfNames - 1;
763             while (High >= Low)
764             {
765                 /* Get new middle value */
766                 Mid = (Low + High) >> 1;
767 
768                 /* Compare name */
769                 Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
770                 if (Ret < 0)
771                 {
772                     /* Update high */
773                     High = Mid - 1;
774                 }
775                 else if (Ret > 0)
776                 {
777                     /* Update low */
778                     Low = Mid + 1;
779                 }
780                 else
781                 {
782                     /* We got it */
783                     break;
784                 }
785             }
786 
787             /* Check if we couldn't find it */
788             if (High < Low)
789             {
790                 DPRINT1("Warning: Driver failed to load, %s not found\n", NameImport->Name);
791                 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
792             }
793 
794             /* Otherwise, this is the ordinal */
795             Ordinal = OrdinalTable[Mid];
796         }
797     }
798 
799     /* Check if the ordinal is invalid */
800     if (Ordinal >= ExportDirectory->NumberOfFunctions)
801     {
802         /* Fail */
803         Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
804     }
805     else
806     {
807         /* In case the forwarder is missing */
808         MissingForwarder = NameBuffer;
809 
810         /* Resolve the address and write it */
811         ExportTable = (PULONG)((ULONG_PTR)DllBase +
812                                ExportDirectory->AddressOfFunctions);
813         Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
814 
815         /* Assume success from now on */
816         Status = STATUS_SUCCESS;
817 
818         /* Check if the function is actually a forwarder */
819         if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
820             (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
821         {
822             /* Now assume failure in case the forwarder doesn't exist */
823             Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
824 
825             /* Build the forwarder name */
826             DllName.Buffer = (PCHAR)Address->u1.Function;
827             DllName.Length = (USHORT)(strchr(DllName.Buffer, '.') -
828                                       DllName.Buffer) +
829                                       sizeof(ANSI_NULL);
830             DllName.MaximumLength = DllName.Length;
831 
832             /* Convert it */
833             if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
834                                                          &DllName,
835                                                          TRUE)))
836             {
837                 /* We failed, just return an error */
838                 return Status;
839             }
840 
841             /* Loop the module list */
842             NextEntry = PsLoadedModuleList.Flink;
843             while (NextEntry != &PsLoadedModuleList)
844             {
845                 /* Get the loader entry */
846                 LdrEntry = CONTAINING_RECORD(NextEntry,
847                                              LDR_DATA_TABLE_ENTRY,
848                                              InLoadOrderLinks);
849 
850                 /* Check if it matches */
851                 if (RtlPrefixUnicodeString(&ForwarderName,
852                                            &LdrEntry->BaseDllName,
853                                            TRUE))
854                 {
855                     /* Get the forwarder export directory */
856                     ForwardExportDirectory =
857                         RtlImageDirectoryEntryToData(LdrEntry->DllBase,
858                                                      TRUE,
859                                                      IMAGE_DIRECTORY_ENTRY_EXPORT,
860                                                      &ForwardExportSize);
861                     if (!ForwardExportDirectory) break;
862 
863                     /* Allocate a name entry */
864                     ForwardLength = strlen(DllName.Buffer + DllName.Length) +
865                                     sizeof(ANSI_NULL);
866                     ForwardName = ExAllocatePoolWithTag(PagedPool,
867                                                         sizeof(*ForwardName) +
868                                                         ForwardLength,
869                                                         TAG_LDR_WSTR);
870                     if (!ForwardName) break;
871 
872                     /* Copy the data */
873                     RtlCopyMemory(&ForwardName->Name[0],
874                                   DllName.Buffer + DllName.Length,
875                                   ForwardLength);
876                     ForwardName->Hint = 0;
877 
878                     /* Set the new address */
879                     ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;
880 
881                     /* Snap the forwarder */
882                     Status = MiSnapThunk(LdrEntry->DllBase,
883                                          ImageBase,
884                                          &ForwardThunk,
885                                          &ForwardThunk,
886                                          ForwardExportDirectory,
887                                          ForwardExportSize,
888                                          TRUE,
889                                          &MissingForwarder);
890 
891                     /* Free the forwarder name and set the thunk */
892                     ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
893                     Address->u1 = ForwardThunk.u1;
894                     break;
895                 }
896 
897                 /* Go to the next entry */
898                 NextEntry = NextEntry->Flink;
899             }
900 
901             /* Free the name */
902             RtlFreeUnicodeString(&ForwarderName);
903         }
904     }
905 
906     /* Return status */
907     return Status;
908 }
909 
910 NTSTATUS
911 NTAPI
912 MmUnloadSystemImage(IN PVOID ImageHandle)
913 {
914     PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
915     PVOID BaseAddress = LdrEntry->DllBase;
916     NTSTATUS Status;
917     STRING TempName;
918     BOOLEAN HadEntry = FALSE;
919 
920     /* Acquire the loader lock */
921     KeEnterCriticalRegion();
922     KeWaitForSingleObject(&MmSystemLoadLock,
923                           WrVirtualMemory,
924                           KernelMode,
925                           FALSE,
926                           NULL);
927 
928     /* Check if this driver was loaded at boot and didn't get imports parsed */
929     if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) goto Done;
930 
931     /* We should still be alive */
932     ASSERT(LdrEntry->LoadCount != 0);
933     LdrEntry->LoadCount--;
934 
935     /* Check if we're still loaded */
936     if (LdrEntry->LoadCount) goto Done;
937 
938     /* We should cleanup... are symbols loaded */
939     if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
940     {
941         /* Create the ANSI name */
942         Status = RtlUnicodeStringToAnsiString(&TempName,
943                                               &LdrEntry->BaseDllName,
944                                               TRUE);
945         if (NT_SUCCESS(Status))
946         {
947             /* Unload the symbols */
948             DbgUnLoadImageSymbols(&TempName,
949                                   BaseAddress,
950                                   (ULONG_PTR)PsGetCurrentProcessId());
951             RtlFreeAnsiString(&TempName);
952         }
953     }
954 
955     /* FIXME: Free the driver */
956     DPRINT1("Leaking driver: %wZ\n", &LdrEntry->BaseDllName);
957     //MmFreeSection(LdrEntry->DllBase);
958 
959     /* Check if we're linked in */
960     if (LdrEntry->InLoadOrderLinks.Flink)
961     {
962         /* Remove us */
963         MiProcessLoaderEntry(LdrEntry, FALSE);
964         HadEntry = TRUE;
965     }
966 
967     /* Dereference and clear the imports */
968     MiDereferenceImports(LdrEntry->LoadedImports);
969     MiClearImports(LdrEntry);
970 
971     /* Check if the entry needs to go away */
972     if (HadEntry)
973     {
974         /* Check if it had a name */
975         if (LdrEntry->FullDllName.Buffer)
976         {
977             /* Free it */
978             ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
979         }
980 
981         /* Check if we had a section */
982         if (LdrEntry->SectionPointer)
983         {
984             /* Dereference it */
985             ObDereferenceObject(LdrEntry->SectionPointer);
986         }
987 
988         /* Free the entry */
989         ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
990     }
991 
992     /* Release the system lock and return */
993 Done:
994     KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
995     KeLeaveCriticalRegion();
996     return STATUS_SUCCESS;
997 }
998 
999 NTSTATUS
1000 NTAPI
1001 MiResolveImageReferences(IN PVOID ImageBase,
1002                          IN PUNICODE_STRING ImageFileDirectory,
1003                          IN PUNICODE_STRING NamePrefix OPTIONAL,
1004                          OUT PCHAR *MissingApi,
1005                          OUT PWCHAR *MissingDriver,
1006                          OUT PLOAD_IMPORTS *LoadImports)
1007 {
1008     static UNICODE_STRING DriversFolderName = RTL_CONSTANT_STRING(L"drivers\\");
1009     PCHAR MissingApiBuffer = *MissingApi, ImportName;
1010     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
1011     ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
1012     PLOAD_IMPORTS LoadedImports, NewImports;
1013     ULONG GdiLink, NormalLink, i;
1014     BOOLEAN ReferenceNeeded, Loaded;
1015     ANSI_STRING TempString;
1016     UNICODE_STRING NameString, DllName;
1017     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
1018     PVOID ImportBase, DllBase;
1019     PLIST_ENTRY NextEntry;
1020     PIMAGE_EXPORT_DIRECTORY ExportDirectory;
1021     NTSTATUS Status;
1022     PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
1023     PAGED_CODE();
1024     DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
1025            __FUNCTION__, ImageBase, ImageFileDirectory);
1026 
1027     /* No name string buffer yet */
1028     NameString.Buffer = NULL;
1029 
1030     /* Assume no imports */
1031     *LoadImports = MM_SYSLDR_NO_IMPORTS;
1032 
1033     /* Get the import descriptor */
1034     ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
1035                                                     TRUE,
1036                                                     IMAGE_DIRECTORY_ENTRY_IMPORT,
1037                                                     &ImportSize);
1038     if (!ImportDescriptor) return STATUS_SUCCESS;
1039 
1040     /* Loop all imports to count them */
1041     for (CurrentImport = ImportDescriptor;
1042          (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
1043          CurrentImport++)
1044     {
1045         /* One more */
1046         ImportCount++;
1047     }
1048 
1049     /* Make sure we have non-zero imports */
1050     if (ImportCount)
1051     {
1052         /* Calculate and allocate the list we'll need */
1053         LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1054         LoadedImports = ExAllocatePoolWithTag(PagedPool,
1055                                               LoadedImportsSize,
1056                                               TAG_LDR_IMPORTS);
1057         if (LoadedImports)
1058         {
1059             /* Zero it */
1060             RtlZeroMemory(LoadedImports, LoadedImportsSize);
1061             LoadedImports->Count = ImportCount;
1062         }
1063     }
1064     else
1065     {
1066         /* No table */
1067         LoadedImports = NULL;
1068     }
1069 
1070     /* Reset the import count and loop descriptors again */
1071     ImportCount = GdiLink = NormalLink = 0;
1072     while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
1073     {
1074         /* Get the name */
1075         ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
1076 
1077         /* Check if this is a GDI driver */
1078         GdiLink = GdiLink |
1079                   !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
1080 
1081         /* We can also allow dxapi (for Windows compat, allow IRT and coverage) */
1082         NormalLink = NormalLink |
1083                      ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
1084                       (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
1085                       (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
1086                       (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
1087 
1088         /* Check if this is a valid GDI driver */
1089         if ((GdiLink) && (NormalLink))
1090         {
1091             /* It's not, it's importing stuff it shouldn't be! */
1092             Status = STATUS_PROCEDURE_NOT_FOUND;
1093             goto Failure;
1094         }
1095 
1096         /* Check for user-mode printer or video card drivers, which don't belong */
1097         if (!(_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
1098             !(_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
1099             !(_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
1100             !(_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
1101             !(_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
1102             !(_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)))
1103         {
1104             /* This is not kernel code */
1105             Status = STATUS_PROCEDURE_NOT_FOUND;
1106             goto Failure;
1107         }
1108 
1109         /* Check if this is a "core" import, which doesn't get referenced */
1110         if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
1111             !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
1112             !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
1113         {
1114             /* Don't reference this */
1115             ReferenceNeeded = FALSE;
1116         }
1117         else
1118         {
1119             /* Reference these modules */
1120             ReferenceNeeded = TRUE;
1121         }
1122 
1123         /* Now setup a unicode string for the import */
1124         RtlInitAnsiString(&TempString, ImportName);
1125         Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
1126         if (!NT_SUCCESS(Status))
1127         {
1128             /* Failed */
1129             goto Failure;
1130         }
1131 
1132         /* We don't support name prefixes yet */
1133         if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
1134 
1135         /* Remember that we haven't loaded the import at this point */
1136 CheckDllState:
1137         Loaded = FALSE;
1138         ImportBase = NULL;
1139 
1140         /* Loop the driver list */
1141         NextEntry = PsLoadedModuleList.Flink;
1142         while (NextEntry != &PsLoadedModuleList)
1143         {
1144             /* Get the loader entry and compare the name */
1145             LdrEntry = CONTAINING_RECORD(NextEntry,
1146                                          LDR_DATA_TABLE_ENTRY,
1147                                          InLoadOrderLinks);
1148             if (RtlEqualUnicodeString(&NameString,
1149                                       &LdrEntry->BaseDllName,
1150                                       TRUE))
1151             {
1152                 /* Get the base address */
1153                 ImportBase = LdrEntry->DllBase;
1154 
1155                 /* Check if we haven't loaded yet, and we need references */
1156                 if (!(Loaded) && (ReferenceNeeded))
1157                 {
1158                     /* Make sure we're not already loading */
1159                     if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1160                     {
1161                         /* Increase the load count */
1162                         LdrEntry->LoadCount++;
1163                     }
1164                 }
1165 
1166                 /* Done, break out */
1167                 break;
1168             }
1169 
1170             /* Go to the next entry */
1171             NextEntry = NextEntry->Flink;
1172         }
1173 
1174         /* Check if we haven't loaded the import yet */
1175         if (!ImportBase)
1176         {
1177             /* Setup the import DLL name */
1178             DllName.MaximumLength = NameString.Length +
1179                                     ImageFileDirectory->Length +
1180                                     sizeof(UNICODE_NULL);
1181             DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
1182                                                    DllName.MaximumLength,
1183                                                    TAG_LDR_WSTR);
1184             if (!DllName.Buffer)
1185             {
1186                 /* We're out of resources */
1187                 Status = STATUS_INSUFFICIENT_RESOURCES;
1188                 goto Failure;
1189             }
1190 
1191             /* Add the import name to the base directory */
1192             RtlCopyUnicodeString(&DllName, ImageFileDirectory);
1193             RtlAppendUnicodeStringToString(&DllName,
1194                                            &NameString);
1195 
1196             /* Load the image */
1197             Status = MmLoadSystemImage(&DllName,
1198                                        NamePrefix,
1199                                        NULL,
1200                                        FALSE,
1201                                        (PVOID *)&DllEntry,
1202                                        &DllBase);
1203 
1204             /* win32k / GDI drivers can also import from system32 folder */
1205             if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) &&
1206                 (MI_IS_SESSION_ADDRESS(ImageBase) || 1)) // HACK
1207             {
1208                 /* Free the old name buffer */
1209                 ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);
1210 
1211                 /* Calculate size for a string the adds 'drivers\' */
1212                 DllName.MaximumLength += DriversFolderName.Length;
1213 
1214                 /* Allocate the new buffer */
1215                 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
1216                                                        DllName.MaximumLength,
1217                                                        TAG_LDR_WSTR);
1218                 if (!DllName.Buffer)
1219                 {
1220                     /* We're out of resources */
1221                     Status = STATUS_INSUFFICIENT_RESOURCES;
1222                     goto Failure;
1223                 }
1224 
1225                 /* Copy image directory and append 'drivers\' folder name */
1226                 RtlCopyUnicodeString(&DllName, ImageFileDirectory);
1227                 RtlAppendUnicodeStringToString(&DllName, &DriversFolderName);
1228 
1229                 /* Now add the import name */
1230                 RtlAppendUnicodeStringToString(&DllName, &NameString);
1231 
1232                 /* Try once again to load the image */
1233                 Status = MmLoadSystemImage(&DllName,
1234                                            NamePrefix,
1235                                            NULL,
1236                                            FALSE,
1237                                            (PVOID *)&DllEntry,
1238                                            &DllBase);
1239             }
1240 
1241             if (!NT_SUCCESS(Status))
1242             {
1243                 /* Fill out the information for the error */
1244                 *MissingDriver = DllName.Buffer;
1245                 *(PULONG)MissingDriver |= 1;
1246                 *MissingApi = NULL;
1247 
1248                 DPRINT1("Failed to load dependency: %wZ\n", &DllName);
1249 
1250                 /* Don't free the name */
1251                 DllName.Buffer = NULL;
1252 
1253                 /* Cleanup and return */
1254                 goto Failure;
1255             }
1256 
1257             /* We can free the DLL Name */
1258             ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);
1259             DllName.Buffer = NULL;
1260 
1261             /* We're now loaded */
1262             Loaded = TRUE;
1263 
1264             /* Sanity check */
1265             ASSERT(DllBase == DllEntry->DllBase);
1266 
1267             /* Call the initialization routines */
1268             Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
1269             if (!NT_SUCCESS(Status))
1270             {
1271                 /* We failed, unload the image */
1272                 MmUnloadSystemImage(DllEntry);
1273                 ERROR_DBGBREAK("MmCallDllInitialize failed with status 0x%x\n", Status);
1274                 Loaded = FALSE;
1275             }
1276 
1277             /* Loop again to make sure that everything is OK */
1278             goto CheckDllState;
1279         }
1280 
1281         /* Check if we're support to reference this import */
1282         if ((ReferenceNeeded) && (LoadedImports))
1283         {
1284             /* Make sure we're not already loading */
1285             if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1286             {
1287                 /* Add the entry */
1288                 LoadedImports->Entry[ImportCount] = LdrEntry;
1289                 ImportCount++;
1290             }
1291         }
1292 
1293         /* Free the import name */
1294         RtlFreeUnicodeString(&NameString);
1295 
1296         /* Set the missing driver name and get the export directory */
1297         *MissingDriver = LdrEntry->BaseDllName.Buffer;
1298         ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
1299                                                        TRUE,
1300                                                        IMAGE_DIRECTORY_ENTRY_EXPORT,
1301                                                        &ExportSize);
1302         if (!ExportDirectory)
1303         {
1304             /* Cleanup and return */
1305             DPRINT1("Warning: Driver failed to load, %S not found\n", *MissingDriver);
1306             Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
1307             goto Failure;
1308         }
1309 
1310         /* Make sure we have an IAT */
1311         if (ImportDescriptor->OriginalFirstThunk)
1312         {
1313             /* Get the first thunks */
1314             OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
1315                                 ImportDescriptor->OriginalFirstThunk);
1316             FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
1317                                  ImportDescriptor->FirstThunk);
1318 
1319             /* Loop the IAT */
1320             while (OrigThunk->u1.AddressOfData)
1321             {
1322                 /* Snap thunk */
1323                 Status = MiSnapThunk(ImportBase,
1324                                      ImageBase,
1325                                      OrigThunk++,
1326                                      FirstThunk++,
1327                                      ExportDirectory,
1328                                      ExportSize,
1329                                      FALSE,
1330                                      MissingApi);
1331                 if (!NT_SUCCESS(Status))
1332                 {
1333                     /* Cleanup and return */
1334                     goto Failure;
1335                 }
1336 
1337                 /* Reset the buffer */
1338                 *MissingApi = MissingApiBuffer;
1339             }
1340         }
1341 
1342         /* Go to the next import */
1343         ImportDescriptor++;
1344     }
1345 
1346     /* Check if we have an import list */
1347     if (LoadedImports)
1348     {
1349         /* Reset the count again, and loop entries */
1350         ImportCount = 0;
1351         for (i = 0; i < LoadedImports->Count; i++)
1352         {
1353             if (LoadedImports->Entry[i])
1354             {
1355                 /* Got an entry, OR it with 1 in case it's the single entry */
1356                 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] |
1357                                       MM_SYSLDR_SINGLE_ENTRY);
1358                 ImportCount++;
1359             }
1360         }
1361 
1362         /* Check if we had no imports */
1363         if (!ImportCount)
1364         {
1365             /* Free the list and set it to no imports */
1366             ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1367             LoadedImports = MM_SYSLDR_NO_IMPORTS;
1368         }
1369         else if (ImportCount == 1)
1370         {
1371             /* Just one entry, we can free the table and only use our entry */
1372             ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1373             LoadedImports = (PLOAD_IMPORTS)ImportEntry;
1374         }
1375         else if (ImportCount != LoadedImports->Count)
1376         {
1377             /* Allocate a new list */
1378             LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1379             NewImports = ExAllocatePoolWithTag(PagedPool,
1380                                                LoadedImportsSize,
1381                                                TAG_LDR_IMPORTS);
1382             if (NewImports)
1383             {
1384                 /* Set count */
1385                 NewImports->Count = 0;
1386 
1387                 /* Loop all the imports */
1388                 for (i = 0; i < LoadedImports->Count; i++)
1389                 {
1390                     /* Make sure it's valid */
1391                     if (LoadedImports->Entry[i])
1392                     {
1393                         /* Copy it */
1394                         NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
1395                         NewImports->Count++;
1396                     }
1397                 }
1398 
1399                 /* Free the old copy */
1400                 ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1401                 LoadedImports = NewImports;
1402             }
1403         }
1404 
1405         /* Return the list */
1406         *LoadImports = LoadedImports;
1407     }
1408 
1409     /* Return success */
1410     return STATUS_SUCCESS;
1411 
1412 Failure:
1413 
1414     /* Cleanup and return */
1415     RtlFreeUnicodeString(&NameString);
1416 
1417     if (LoadedImports)
1418     {
1419         MiDereferenceImports(LoadedImports);
1420         ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1421     }
1422 
1423     return Status;
1424 }
1425 
1426 VOID
1427 NTAPI
1428 MiFreeInitializationCode(IN PVOID InitStart,
1429                          IN PVOID InitEnd)
1430 {
1431     PMMPTE PointerPte;
1432     PFN_NUMBER PagesFreed;
1433 
1434     /* Get the start PTE */
1435     PointerPte = MiAddressToPte(InitStart);
1436     ASSERT(MI_IS_PHYSICAL_ADDRESS(InitStart) == FALSE);
1437 
1438     /*  Compute the number of pages we expect to free */
1439     PagesFreed = (PFN_NUMBER)(MiAddressToPte(InitEnd) - PointerPte);
1440 
1441     /* Try to actually free them */
1442     PagesFreed = MiDeleteSystemPageableVm(PointerPte,
1443                                           PagesFreed,
1444                                           0,
1445                                           NULL);
1446 }
1447 
1448 CODE_SEG("INIT")
1449 VOID
1450 NTAPI
1451 MiFindInitializationCode(OUT PVOID *StartVa,
1452                          OUT PVOID *EndVa)
1453 {
1454     ULONG Size, SectionCount, Alignment;
1455     PLDR_DATA_TABLE_ENTRY LdrEntry;
1456     ULONG_PTR DllBase, InitStart, InitEnd, ImageEnd, InitCode;
1457     PLIST_ENTRY NextEntry;
1458     PIMAGE_NT_HEADERS NtHeader;
1459     PIMAGE_SECTION_HEADER Section, LastSection, InitSection;
1460     BOOLEAN InitFound;
1461     DBG_UNREFERENCED_LOCAL_VARIABLE(InitSection);
1462 
1463     /* So we don't free our own code yet */
1464     InitCode = (ULONG_PTR)&MiFindInitializationCode;
1465 
1466     /* Assume failure */
1467     *StartVa = NULL;
1468 
1469     /* Acquire the necessary locks while we loop the list */
1470     KeEnterCriticalRegion();
1471     KeWaitForSingleObject(&MmSystemLoadLock,
1472                           WrVirtualMemory,
1473                           KernelMode,
1474                           FALSE,
1475                           NULL);
1476     ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
1477 
1478     /* Loop all loaded modules */
1479     NextEntry = PsLoadedModuleList.Flink;
1480     while (NextEntry != &PsLoadedModuleList)
1481     {
1482         /* Get the loader entry and its DLL base */
1483         LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1484         DllBase = (ULONG_PTR)LdrEntry->DllBase;
1485 
1486         /* Only process boot loaded images. Other drivers are processed by
1487            MmFreeDriverInitialization */
1488         if (LdrEntry->Flags & LDRP_MM_LOADED)
1489         {
1490             /* Keep going */
1491             NextEntry = NextEntry->Flink;
1492             continue;
1493         }
1494 
1495         /* Get the NT header */
1496         NtHeader = RtlImageNtHeader((PVOID)DllBase);
1497         if (!NtHeader)
1498         {
1499             /* Keep going */
1500             NextEntry = NextEntry->Flink;
1501             continue;
1502         }
1503 
1504         /* Get the first section, the section count, and scan them all */
1505         Section = IMAGE_FIRST_SECTION(NtHeader);
1506         SectionCount = NtHeader->FileHeader.NumberOfSections;
1507         InitStart = 0;
1508         while (SectionCount > 0)
1509         {
1510             /* Assume failure */
1511             InitFound = FALSE;
1512 
1513             /* Is this the INIT section or a discardable section? */
1514             if ((strncmp((PCCH)Section->Name, "INIT", 5) == 0) ||
1515                 ((Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)))
1516             {
1517                 /* Remember this */
1518                 InitFound = TRUE;
1519                 InitSection = Section;
1520             }
1521 
1522             if (InitFound)
1523             {
1524                 /* Pick the biggest size -- either raw or virtual */
1525                 Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
1526 
1527                 /* Read the section alignment */
1528                 Alignment = NtHeader->OptionalHeader.SectionAlignment;
1529 
1530                 /* Get the start and end addresses */
1531                 InitStart = DllBase + Section->VirtualAddress;
1532                 InitEnd = ALIGN_UP_BY(InitStart + Size, Alignment);
1533 
1534                 /* Align the addresses to PAGE_SIZE */
1535                 InitStart = ALIGN_UP_BY(InitStart, PAGE_SIZE);
1536                 InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
1537 
1538                 /* Have we reached the last section? */
1539                 if (SectionCount == 1)
1540                 {
1541                     /* Remember this */
1542                     LastSection = Section;
1543                 }
1544                 else
1545                 {
1546                     /* We have not, loop all the sections */
1547                     LastSection = NULL;
1548                     do
1549                     {
1550                         /* Keep going until we find a non-discardable section range */
1551                         SectionCount--;
1552                         Section++;
1553                         if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
1554                         {
1555                             /* Discardable, so record it, then keep going */
1556                             LastSection = Section;
1557                         }
1558                         else
1559                         {
1560                             /* Non-contigous discard flag, or no flag, break out */
1561                             break;
1562                         }
1563                     }
1564                     while (SectionCount > 1);
1565                 }
1566 
1567                 /* Have we found a discardable or init section? */
1568                 if (LastSection)
1569                 {
1570                     /* Pick the biggest size -- either raw or virtual */
1571                     Size = max(LastSection->SizeOfRawData, LastSection->Misc.VirtualSize);
1572 
1573                     /* Use this as the end of the section address */
1574                     InitEnd = DllBase + LastSection->VirtualAddress + Size;
1575 
1576                     /* Have we reached the last section yet? */
1577                     if (SectionCount != 1)
1578                     {
1579                         /* Then align this accross the session boundary */
1580                         InitEnd = ALIGN_UP_BY(InitEnd, Alignment);
1581                         InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
1582                     }
1583                 }
1584 
1585                 /* Make sure we don't let the init section go past the image */
1586                 ImageEnd = DllBase + LdrEntry->SizeOfImage;
1587                 if (InitEnd > ImageEnd) InitEnd = ALIGN_UP_BY(ImageEnd, PAGE_SIZE);
1588 
1589                 /* Make sure we have a valid, non-zero init section */
1590                 if (InitStart < InitEnd)
1591                 {
1592                     /* Make sure we are not within this code itself */
1593                     if ((InitCode >= InitStart) && (InitCode < InitEnd))
1594                     {
1595                         /* Return it, we can't free ourselves now */
1596                         ASSERT(*StartVa == 0);
1597                         *StartVa = (PVOID)InitStart;
1598                         *EndVa = (PVOID)InitEnd;
1599                     }
1600                     else
1601                     {
1602                         /* This isn't us -- go ahead and free it */
1603                         ASSERT(MI_IS_PHYSICAL_ADDRESS((PVOID)InitStart) == FALSE);
1604                         DPRINT("Freeing init code: %p-%p ('%wZ' @%p : '%s')\n",
1605                                (PVOID)InitStart,
1606                                (PVOID)InitEnd,
1607                                &LdrEntry->BaseDllName,
1608                                LdrEntry->DllBase,
1609                                InitSection->Name);
1610                         MiFreeInitializationCode((PVOID)InitStart, (PVOID)InitEnd);
1611                     }
1612                 }
1613             }
1614 
1615             /* Move to the next section */
1616             SectionCount--;
1617             Section++;
1618         }
1619 
1620         /* Move to the next module */
1621         NextEntry = NextEntry->Flink;
1622     }
1623 
1624     /* Release the locks and return */
1625     ExReleaseResourceLite(&PsLoadedModuleResource);
1626     KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
1627     KeLeaveCriticalRegion();
1628 }
1629 
1630 /*
1631  * Note: This function assumes that all discardable sections are at the end of
1632  * the PE file. It searches backwards until it finds the non-discardable section
1633  */
1634 VOID
1635 NTAPI
1636 MmFreeDriverInitialization(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
1637 {
1638     PMMPTE StartPte, EndPte;
1639     PFN_NUMBER PageCount;
1640     PVOID DllBase;
1641     ULONG i;
1642     PIMAGE_NT_HEADERS NtHeader;
1643     PIMAGE_SECTION_HEADER Section, DiscardSection;
1644 
1645     /* Get the base address and the page count */
1646     DllBase = LdrEntry->DllBase;
1647     PageCount = LdrEntry->SizeOfImage >> PAGE_SHIFT;
1648 
1649     /* Get the last PTE in this image */
1650     EndPte = MiAddressToPte(DllBase) + PageCount;
1651 
1652     /* Get the NT header */
1653     NtHeader = RtlImageNtHeader(DllBase);
1654     if (!NtHeader) return;
1655 
1656     /* Get the last section and loop each section backwards */
1657     Section = IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections;
1658     DiscardSection = NULL;
1659     for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
1660     {
1661         /* Go back a section and check if it's discardable */
1662         Section--;
1663         if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
1664         {
1665             /* It is, select it for freeing */
1666             DiscardSection = Section;
1667         }
1668         else
1669         {
1670             /* No more discardable sections exist, bail out */
1671             break;
1672         }
1673     }
1674 
1675     /* Bail out if there's nothing to free */
1676     if (!DiscardSection) return;
1677 
1678     /* Push the DLL base to the first disacrable section, and get its PTE */
1679     DllBase = (PVOID)ROUND_TO_PAGES((ULONG_PTR)DllBase + DiscardSection->VirtualAddress);
1680     ASSERT(MI_IS_PHYSICAL_ADDRESS(DllBase) == FALSE);
1681     StartPte = MiAddressToPte(DllBase);
1682 
1683     /* Check how many pages to free total */
1684     PageCount = (PFN_NUMBER)(EndPte - StartPte);
1685     if (!PageCount) return;
1686 
1687     /* Delete this many PTEs */
1688     MiDeleteSystemPageableVm(StartPte, PageCount, 0, NULL);
1689 }
1690 
1691 CODE_SEG("INIT")
1692 VOID
1693 NTAPI
1694 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1695 {
1696     PLIST_ENTRY NextEntry;
1697     ULONG i = 0;
1698     PIMAGE_NT_HEADERS NtHeader;
1699     PLDR_DATA_TABLE_ENTRY LdrEntry;
1700     PIMAGE_FILE_HEADER FileHeader;
1701     BOOLEAN ValidRelocs;
1702     PIMAGE_DATA_DIRECTORY DataDirectory;
1703     PVOID DllBase, NewImageAddress;
1704     NTSTATUS Status;
1705     PMMPTE PointerPte, StartPte, LastPte;
1706     PFN_COUNT PteCount;
1707     PMMPFN Pfn1;
1708     MMPTE TempPte, OldPte;
1709 
1710     /* Loop driver list */
1711     for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1712          NextEntry != &LoaderBlock->LoadOrderListHead;
1713          NextEntry = NextEntry->Flink)
1714     {
1715         /* Get the loader entry and NT header */
1716         LdrEntry = CONTAINING_RECORD(NextEntry,
1717                                      LDR_DATA_TABLE_ENTRY,
1718                                      InLoadOrderLinks);
1719         NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1720 
1721         /* Debug info */
1722         DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1723                 LdrEntry->DllBase,
1724                 (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
1725                 &LdrEntry->FullDllName);
1726 
1727         /* Get the first PTE and the number of PTEs we'll need */
1728         PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
1729         PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
1730         LastPte = StartPte + PteCount;
1731 
1732 #if MI_TRACE_PFNS
1733         /* Loop the PTEs */
1734         while (PointerPte < LastPte)
1735         {
1736             ULONG len;
1737             ASSERT(PointerPte->u.Hard.Valid == 1);
1738             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1739             len = wcslen(LdrEntry->BaseDllName.Buffer) * sizeof(WCHAR);
1740             snprintf(Pfn1->ProcessName, min(16, len), "%S", LdrEntry->BaseDllName.Buffer);
1741             PointerPte++;
1742         }
1743 #endif
1744         /* Skip kernel and HAL */
1745         /* ROS HACK: Skip BOOTVID/KDCOM too */
1746         i++;
1747         if (i <= 4) continue;
1748 
1749         /* Skip non-drivers */
1750         if (!NtHeader) continue;
1751 
1752         /* Get the file header and make sure we can relocate */
1753         FileHeader = &NtHeader->FileHeader;
1754         if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
1755         if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
1756             IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
1757 
1758         /* Everything made sense until now, check the relocation section too */
1759         DataDirectory = &NtHeader->OptionalHeader.
1760                         DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1761         if (!DataDirectory->VirtualAddress)
1762         {
1763             /* We don't really have relocations */
1764             ValidRelocs = FALSE;
1765         }
1766         else
1767         {
1768             /* Make sure the size is valid */
1769             if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1770                 LdrEntry->SizeOfImage)
1771             {
1772                 /* They're not, skip */
1773                 continue;
1774             }
1775 
1776             /* We have relocations */
1777             ValidRelocs = TRUE;
1778         }
1779 
1780         /* Remember the original address */
1781         DllBase = LdrEntry->DllBase;
1782 
1783         /* Loop the PTEs */
1784         PointerPte = StartPte;
1785         while (PointerPte < LastPte)
1786         {
1787             /* Mark the page modified in the PFN database */
1788             ASSERT(PointerPte->u.Hard.Valid == 1);
1789             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1790             ASSERT(Pfn1->u3.e1.Rom == 0);
1791             Pfn1->u3.e1.Modified = TRUE;
1792 
1793             /* Next */
1794             PointerPte++;
1795         }
1796 
1797         /* Now reserve system PTEs for the image */
1798         PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
1799         if (!PointerPte)
1800         {
1801             /* Shouldn't happen */
1802             ERROR_FATAL("[Mm0]: Couldn't allocate driver section!\n");
1803             return;
1804         }
1805 
1806         /* This is the new virtual address for the module */
1807         LastPte = PointerPte + PteCount;
1808         NewImageAddress = MiPteToAddress(PointerPte);
1809 
1810         /* Sanity check */
1811         DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1812         ASSERT(ExpInitializationPhase == 0);
1813 
1814         /* Loop the new driver PTEs */
1815         TempPte = ValidKernelPte;
1816         while (PointerPte < LastPte)
1817         {
1818             /* Copy the old data */
1819             OldPte = *StartPte;
1820             ASSERT(OldPte.u.Hard.Valid == 1);
1821 
1822             /* Set page number from the loader's memory */
1823             TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;
1824 
1825             /* Write it */
1826             MI_WRITE_VALID_PTE(PointerPte, TempPte);
1827 
1828             /* Move on */
1829             PointerPte++;
1830             StartPte++;
1831         }
1832 
1833         /* Update position */
1834         PointerPte -= PteCount;
1835 
1836         /* Sanity check */
1837         ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1838 
1839         /* Set the image base to the address where the loader put it */
1840         NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1841 
1842         /* Check if we had relocations */
1843         if (ValidRelocs)
1844         {
1845             /* Relocate the image */
1846             Status = LdrRelocateImageWithBias(NewImageAddress,
1847                                               0,
1848                                               "SYSLDR",
1849                                               STATUS_SUCCESS,
1850                                               STATUS_CONFLICTING_ADDRESSES,
1851                                               STATUS_INVALID_IMAGE_FORMAT);
1852             if (!NT_SUCCESS(Status))
1853             {
1854                 /* This shouldn't happen */
1855                 ERROR_FATAL("Relocations failed!\n");
1856                 return;
1857             }
1858         }
1859 
1860         /* Update the loader entry */
1861         LdrEntry->DllBase = NewImageAddress;
1862 
1863         /* Update the thunks */
1864         DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1865         MiUpdateThunks(LoaderBlock,
1866                        DllBase,
1867                        NewImageAddress,
1868                        LdrEntry->SizeOfImage);
1869 
1870         /* Update the loader entry */
1871         LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
1872         LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1873                                 NtHeader->OptionalHeader.AddressOfEntryPoint);
1874         LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;
1875 
1876         /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
1877     }
1878 }
1879 
1880 CODE_SEG("INIT")
1881 NTSTATUS
1882 NTAPI
1883 MiBuildImportsForBootDrivers(VOID)
1884 {
1885     PLIST_ENTRY NextEntry, NextEntry2;
1886     PLDR_DATA_TABLE_ENTRY LdrEntry, KernelEntry, HalEntry, LdrEntry2, LastEntry;
1887     PLDR_DATA_TABLE_ENTRY* EntryArray;
1888     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
1889     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
1890     PLOAD_IMPORTS LoadedImports;
1891     ULONG LoadedImportsSize, ImportSize;
1892     PULONG_PTR ImageThunk;
1893     ULONG_PTR DllBase, DllEnd;
1894     ULONG Modules = 0, i, j = 0;
1895     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
1896 
1897     /* Initialize variables */
1898     KernelEntry = HalEntry = LastEntry = NULL;
1899 
1900     /* Loop the loaded module list... we are early enough that no lock is needed */
1901     NextEntry = PsLoadedModuleList.Flink;
1902     while (NextEntry != &PsLoadedModuleList)
1903     {
1904         /* Get the entry */
1905         LdrEntry = CONTAINING_RECORD(NextEntry,
1906                                      LDR_DATA_TABLE_ENTRY,
1907                                      InLoadOrderLinks);
1908 
1909         /* Check if it's the kernel or HAL */
1910         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
1911         {
1912             /* Found it */
1913             KernelEntry = LdrEntry;
1914         }
1915         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
1916         {
1917             /* Found it */
1918             HalEntry = LdrEntry;
1919         }
1920 
1921         /* Check if this is a driver DLL */
1922         if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
1923         {
1924             /* Check if this is the HAL or kernel */
1925             if ((LdrEntry == HalEntry) || (LdrEntry == KernelEntry))
1926             {
1927                 /* Add a reference */
1928                 LdrEntry->LoadCount = 1;
1929             }
1930             else
1931             {
1932                 /* No referencing needed */
1933                 LdrEntry->LoadCount = 0;
1934             }
1935         }
1936         else
1937         {
1938             /* Add a reference for all other modules as well */
1939             LdrEntry->LoadCount = 1;
1940         }
1941 
1942         /* Remember this came from the loader */
1943         LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
1944 
1945         /* Keep looping */
1946         NextEntry = NextEntry->Flink;
1947         Modules++;
1948     }
1949 
1950     /* We must have at least found the kernel and HAL */
1951     if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;
1952 
1953     /* Allocate the list */
1954     EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), TAG_LDR_IMPORTS);
1955     if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;
1956 
1957     /* Loop the loaded module list again */
1958     NextEntry = PsLoadedModuleList.Flink;
1959     while (NextEntry != &PsLoadedModuleList)
1960     {
1961         /* Get the entry */
1962         LdrEntry = CONTAINING_RECORD(NextEntry,
1963                                      LDR_DATA_TABLE_ENTRY,
1964                                      InLoadOrderLinks);
1965 #ifdef _WORKING_LOADER_
1966         /* Get its imports */
1967         ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1968                                                   TRUE,
1969                                                   IMAGE_DIRECTORY_ENTRY_IAT,
1970                                                   &ImportSize);
1971         if (!ImageThunk)
1972 #else
1973         /* Get its imports */
1974         ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1975                                                         TRUE,
1976                                                         IMAGE_DIRECTORY_ENTRY_IMPORT,
1977                                                         &ImportSize);
1978         if (!ImportDescriptor)
1979 #endif
1980         {
1981             /* None present */
1982             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
1983             NextEntry = NextEntry->Flink;
1984             continue;
1985         }
1986 
1987         /* Clear the list and count the number of IAT thunks */
1988         RtlZeroMemory(EntryArray, Modules * sizeof(PVOID));
1989 #ifdef _WORKING_LOADER_
1990         ImportSize /= sizeof(ULONG_PTR);
1991 
1992         /* Scan the thunks */
1993         for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
1994 #else
1995         DllBase = DllEnd = i = 0;
1996         while ((ImportDescriptor->Name) &&
1997                (ImportDescriptor->OriginalFirstThunk))
1998         {
1999             /* Get the image thunk */
2000             ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
2001                                  ImportDescriptor->FirstThunk);
2002             while (*ImageThunk)
2003 #endif
2004             {
2005             /* Do we already have an address? */
2006             if (DllBase)
2007             {
2008                 /* Is the thunk in the same address? */
2009                 if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
2010                 {
2011                     /* Skip it, we already have a reference for it */
2012                     ASSERT(EntryArray[j]);
2013                     ImageThunk++;
2014                     continue;
2015                 }
2016             }
2017 
2018             /* Loop the loaded module list to locate this address owner */
2019             j = 0;
2020             NextEntry2 = PsLoadedModuleList.Flink;
2021             while (NextEntry2 != &PsLoadedModuleList)
2022             {
2023                 /* Get the entry */
2024                 LdrEntry2 = CONTAINING_RECORD(NextEntry2,
2025                                               LDR_DATA_TABLE_ENTRY,
2026                                               InLoadOrderLinks);
2027 
2028                 /* Get the address range for this module */
2029                 DllBase = (ULONG_PTR)LdrEntry2->DllBase;
2030                 DllEnd = DllBase + LdrEntry2->SizeOfImage;
2031 
2032                 /* Check if this IAT entry matches it */
2033                 if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
2034                 {
2035                     /* Save it */
2036                     //DPRINT1("Found imported dll: %wZ\n", &LdrEntry2->BaseDllName);
2037                     EntryArray[j] = LdrEntry2;
2038                     break;
2039                 }
2040 
2041                 /* Keep searching */
2042                 NextEntry2 = NextEntry2->Flink;
2043                 j++;
2044             }
2045 
2046             /* Do we have a thunk outside the range? */
2047             if ((*ImageThunk < DllBase) || (*ImageThunk >= DllEnd))
2048             {
2049                 /* Could be 0... */
2050                 if (*ImageThunk)
2051                 {
2052                     /* Should not be happening */
2053                     ERROR_FATAL("Broken IAT entry for %p at %p (%lx)\n",
2054                                 LdrEntry, ImageThunk, *ImageThunk);
2055                 }
2056 
2057                 /* Reset if we hit this */
2058                 DllBase = 0;
2059             }
2060 #ifndef _WORKING_LOADER_
2061             ImageThunk++;
2062             }
2063 
2064             i++;
2065             ImportDescriptor++;
2066 #endif
2067         }
2068 
2069         /* Now scan how many imports we really have */
2070         for (i = 0, ImportSize = 0; i < Modules; i++)
2071         {
2072             /* Skip HAL and kernel */
2073             if ((EntryArray[i]) &&
2074                 (EntryArray[i] != HalEntry) &&
2075                 (EntryArray[i] != KernelEntry))
2076             {
2077                 /* A valid reference */
2078                 LastEntry = EntryArray[i];
2079                 ImportSize++;
2080             }
2081         }
2082 
2083         /* Do we have any imports after all? */
2084         if (!ImportSize)
2085         {
2086             /* No */
2087             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
2088         }
2089         else if (ImportSize == 1)
2090         {
2091             /* A single entry import */
2092             LdrEntry->LoadedImports = (PVOID)((ULONG_PTR)LastEntry | MM_SYSLDR_SINGLE_ENTRY);
2093             LastEntry->LoadCount++;
2094         }
2095         else
2096         {
2097             /* We need an import table */
2098             LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
2099             LoadedImports = ExAllocatePoolWithTag(PagedPool,
2100                                                   LoadedImportsSize,
2101                                                   TAG_LDR_IMPORTS);
2102             ASSERT(LoadedImports);
2103 
2104             /* Save the count */
2105             LoadedImports->Count = ImportSize;
2106 
2107             /* Now copy all imports */
2108             for (i = 0, j = 0; i < Modules; i++)
2109             {
2110                 /* Skip HAL and kernel */
2111                 if ((EntryArray[i]) &&
2112                     (EntryArray[i] != HalEntry) &&
2113                     (EntryArray[i] != KernelEntry))
2114                 {
2115                     /* A valid reference */
2116                     //DPRINT1("Found valid entry: %p\n", EntryArray[i]);
2117                     LoadedImports->Entry[j] = EntryArray[i];
2118                     EntryArray[i]->LoadCount++;
2119                     j++;
2120                 }
2121             }
2122 
2123             /* Should had as many entries as we expected */
2124             ASSERT(j == ImportSize);
2125             LdrEntry->LoadedImports = LoadedImports;
2126         }
2127 
2128         /* Next */
2129         NextEntry = NextEntry->Flink;
2130     }
2131 
2132     /* Free the initial array */
2133     ExFreePoolWithTag(EntryArray, TAG_LDR_IMPORTS);
2134 
2135     /* FIXME: Might not need to keep the HAL/Kernel imports around */
2136 
2137     /* Kernel and HAL are loaded at boot */
2138     KernelEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
2139     HalEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
2140 
2141     /* All worked well */
2142     return STATUS_SUCCESS;
2143 }
2144 
2145 CODE_SEG("INIT")
2146 VOID
2147 NTAPI
2148 MiLocateKernelSections(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
2149 {
2150     ULONG_PTR DllBase;
2151     PIMAGE_NT_HEADERS NtHeaders;
2152     PIMAGE_SECTION_HEADER SectionHeader;
2153     ULONG Sections, Size;
2154 
2155     /* Get the kernel section header */
2156     DllBase = (ULONG_PTR)LdrEntry->DllBase;
2157     NtHeaders = RtlImageNtHeader((PVOID)DllBase);
2158     SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
2159 
2160     /* Loop all the sections */
2161     for (Sections = NtHeaders->FileHeader.NumberOfSections;
2162          Sections > 0; --Sections, ++SectionHeader)
2163     {
2164         /* Grab the size of the section */
2165         Size = max(SectionHeader->SizeOfRawData, SectionHeader->Misc.VirtualSize);
2166 
2167         /* Check for .RSRC section */
2168         if (*(PULONG)SectionHeader->Name == 'rsr.')
2169         {
2170             /* Remember the PTEs so we can modify them later */
2171             MiKernelResourceStartPte = MiAddressToPte(DllBase +
2172                                                       SectionHeader->VirtualAddress);
2173             MiKernelResourceEndPte = MiAddressToPte(ROUND_TO_PAGES(DllBase +
2174                                                     SectionHeader->VirtualAddress + Size));
2175         }
2176         else if (*(PULONG)SectionHeader->Name == 'LOOP')
2177         {
2178             /* POOLCODE vs. POOLMI */
2179             if (*(PULONG)&SectionHeader->Name[4] == 'EDOC')
2180             {
2181                 /* Found Ex* Pool code */
2182                 ExPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
2183                 ExPoolCodeEnd = ExPoolCodeStart + Size;
2184             }
2185             else if (*(PUSHORT)&SectionHeader->Name[4] == 'MI')
2186             {
2187                 /* Found Mm* Pool code */
2188                 MmPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
2189                 MmPoolCodeEnd = MmPoolCodeStart + Size;
2190             }
2191         }
2192         else if ((*(PULONG)SectionHeader->Name == 'YSIM') &&
2193                  (*(PULONG)&SectionHeader->Name[4] == 'ETPS'))
2194         {
2195             /* Found MISYSPTE (Mm System PTE code) */
2196             MmPteCodeStart = DllBase + SectionHeader->VirtualAddress;
2197             MmPteCodeEnd = MmPteCodeStart + Size;
2198         }
2199     }
2200 }
2201 
2202 CODE_SEG("INIT")
2203 BOOLEAN
2204 NTAPI
2205 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
2206 {
2207     PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
2208     PLIST_ENTRY ListHead, NextEntry;
2209     ULONG EntrySize;
2210 
2211     /* Setup the loaded module list and locks */
2212     ExInitializeResourceLite(&PsLoadedModuleResource);
2213     KeInitializeSpinLock(&PsLoadedModuleSpinLock);
2214     InitializeListHead(&PsLoadedModuleList);
2215 
2216     /* Get loop variables and the kernel entry */
2217     ListHead = &LoaderBlock->LoadOrderListHead;
2218     NextEntry = ListHead->Flink;
2219     LdrEntry = CONTAINING_RECORD(NextEntry,
2220                                  LDR_DATA_TABLE_ENTRY,
2221                                  InLoadOrderLinks);
2222     PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
2223 
2224     /* Locate resource section, pool code, and system pte code */
2225     MiLocateKernelSections(LdrEntry);
2226 
2227     /* Loop the loader block */
2228     while (NextEntry != ListHead)
2229     {
2230         /* Get the loader entry */
2231         LdrEntry = CONTAINING_RECORD(NextEntry,
2232                                      LDR_DATA_TABLE_ENTRY,
2233                                      InLoadOrderLinks);
2234 
2235         /* FIXME: ROS HACK. Make sure this is a driver */
2236         if (!RtlImageNtHeader(LdrEntry->DllBase))
2237         {
2238             /* Skip this entry */
2239             NextEntry = NextEntry->Flink;
2240             continue;
2241         }
2242 
2243         /* Calculate the size we'll need and allocate a copy */
2244         EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
2245                     LdrEntry->BaseDllName.MaximumLength +
2246                     sizeof(UNICODE_NULL);
2247         NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
2248         if (!NewEntry) return FALSE;
2249 
2250         /* Copy the entry over */
2251         *NewEntry = *LdrEntry;
2252 
2253         /* Allocate the name */
2254         NewEntry->FullDllName.Buffer =
2255             ExAllocatePoolWithTag(PagedPool,
2256                                   LdrEntry->FullDllName.MaximumLength +
2257                                       sizeof(UNICODE_NULL),
2258                                   TAG_LDR_WSTR);
2259         if (!NewEntry->FullDllName.Buffer)
2260         {
2261             ExFreePoolWithTag(NewEntry, TAG_MODULE_OBJECT);
2262             return FALSE;
2263         }
2264 
2265         /* Set the base name */
2266         NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
2267 
2268         /* Copy the full and base name */
2269         RtlCopyMemory(NewEntry->FullDllName.Buffer,
2270                       LdrEntry->FullDllName.Buffer,
2271                       LdrEntry->FullDllName.MaximumLength);
2272         RtlCopyMemory(NewEntry->BaseDllName.Buffer,
2273                       LdrEntry->BaseDllName.Buffer,
2274                       LdrEntry->BaseDllName.MaximumLength);
2275 
2276         /* Null-terminate the base name */
2277         NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
2278                                      sizeof(WCHAR)] = UNICODE_NULL;
2279 
2280         /* Insert the entry into the list */
2281         InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
2282         NextEntry = NextEntry->Flink;
2283     }
2284 
2285     /* Build the import lists for the boot drivers */
2286     MiBuildImportsForBootDrivers();
2287 
2288     /* We're done */
2289     return TRUE;
2290 }
2291 
2292 BOOLEAN
2293 NTAPI
2294 MmChangeKernelResourceSectionProtection(IN ULONG_PTR ProtectionMask)
2295 {
2296     PMMPTE PointerPte;
2297     MMPTE TempPte;
2298 
2299     /* Don't do anything if the resource section is already writable */
2300     if (MiKernelResourceStartPte == NULL || MiKernelResourceEndPte == NULL)
2301         return FALSE;
2302 
2303     /* If the resource section is physical, we cannot change its protection */
2304     if (MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(MiKernelResourceStartPte)))
2305         return FALSE;
2306 
2307     /* Loop the PTEs */
2308     for (PointerPte = MiKernelResourceStartPte; PointerPte < MiKernelResourceEndPte; ++PointerPte)
2309     {
2310         /* Read the PTE */
2311         TempPte = *PointerPte;
2312 
2313         /* Update the protection */
2314         MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, PointerPte, ProtectionMask, TempPte.u.Hard.PageFrameNumber);
2315         MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2316     }
2317 
2318     /* Only flush the current processor's TLB */
2319     KeFlushCurrentTb();
2320     return TRUE;
2321 }
2322 
2323 VOID
2324 NTAPI
2325 MmMakeKernelResourceSectionWritable(VOID)
2326 {
2327     /* Don't do anything if the resource section is already writable */
2328     if (MiKernelResourceStartPte == NULL || MiKernelResourceEndPte == NULL)
2329         return;
2330 
2331     /* If the resource section is physical, we cannot change its protection */
2332     if (MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(MiKernelResourceStartPte)))
2333         return;
2334 
2335     if (MmChangeKernelResourceSectionProtection(MM_READWRITE))
2336     {
2337         /*
2338          * Invalidate the cached resource section PTEs
2339          * so as to not change its protection again later.
2340          */
2341         MiKernelResourceStartPte = NULL;
2342         MiKernelResourceEndPte = NULL;
2343     }
2344 }
2345 
2346 LOGICAL
2347 NTAPI
2348 MiUseLargeDriverPage(IN ULONG NumberOfPtes,
2349                      IN OUT PVOID *ImageBaseAddress,
2350                      IN PUNICODE_STRING BaseImageName,
2351                      IN BOOLEAN BootDriver)
2352 {
2353     PLIST_ENTRY NextEntry;
2354     BOOLEAN DriverFound = FALSE;
2355     PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
2356     ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
2357     ASSERT(*ImageBaseAddress >= MmSystemRangeStart);
2358 
2359 #ifdef _X86_
2360     if (!(KeFeatureBits & KF_LARGE_PAGE)) return FALSE;
2361     if (!(__readcr4() & CR4_PSE)) return FALSE;
2362 #endif
2363 
2364     /* Make sure there's enough system PTEs for a large page driver */
2365     if (MmTotalFreeSystemPtes[SystemPteSpace] < (16 * (PDE_MAPPED_VA >> PAGE_SHIFT)))
2366     {
2367         return FALSE;
2368     }
2369 
2370     /* This happens if the registry key had a "*" (wildcard) in it */
2371     if (MiLargePageAllDrivers == 0)
2372     {
2373         /* It didn't, so scan the list */
2374         NextEntry = MiLargePageDriverList.Flink;
2375         while (NextEntry != &MiLargePageDriverList)
2376         {
2377             /* Check if the driver name matches */
2378             LargePageDriverEntry = CONTAINING_RECORD(NextEntry,
2379                                                      MI_LARGE_PAGE_DRIVER_ENTRY,
2380                                                      Links);
2381             if (RtlEqualUnicodeString(BaseImageName,
2382                                       &LargePageDriverEntry->BaseName,
2383                                       TRUE))
2384             {
2385                 /* Enable large pages for this driver */
2386                 DriverFound = TRUE;
2387                 break;
2388             }
2389 
2390             /* Keep trying */
2391             NextEntry = NextEntry->Flink;
2392         }
2393 
2394         /* If we didn't find the driver, it doesn't need large pages */
2395         if (DriverFound == FALSE) return FALSE;
2396     }
2397 
2398     /* Nothing to do yet */
2399     DPRINT1("Large pages not supported!\n");
2400     return FALSE;
2401 }
2402 
2403 VOID
2404 NTAPI
2405 MiSetSystemCodeProtection(
2406     _In_ PMMPTE FirstPte,
2407     _In_ PMMPTE LastPte,
2408     _In_ ULONG Protection)
2409 {
2410     PMMPTE PointerPte;
2411     MMPTE TempPte;
2412 
2413     /* Loop the PTEs */
2414     for (PointerPte = FirstPte; PointerPte <= LastPte; PointerPte++)
2415     {
2416         /* Read the PTE */
2417         TempPte = *PointerPte;
2418 
2419         /* Make sure it's valid */
2420         if (TempPte.u.Hard.Valid != 1)
2421         {
2422             DPRINT1("CORE-16449: FirstPte=%p, LastPte=%p, Protection=%lx\n", FirstPte, LastPte, Protection);
2423             DPRINT1("CORE-16449: PointerPte=%p, TempPte=%lx\n", PointerPte, TempPte.u.Long);
2424             DPRINT1("CORE-16449: Please issue the 'mod' and 'bt' (KDBG) or 'lm' and 'kp' (WinDbg) commands. Then report this in Jira.\n");
2425             ASSERT(TempPte.u.Hard.Valid == 1);
2426             break;
2427         }
2428 
2429         /* Update the protection */
2430         TempPte.u.Hard.Write = BooleanFlagOn(Protection, IMAGE_SCN_MEM_WRITE);
2431 #if _MI_HAS_NO_EXECUTE
2432         TempPte.u.Hard.NoExecute = !BooleanFlagOn(Protection, IMAGE_SCN_MEM_EXECUTE);
2433 #endif
2434 
2435         MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2436     }
2437 
2438     /* Flush it all */
2439     KeFlushEntireTb(TRUE, TRUE);
2440 
2441     return;
2442 }
2443 
2444 VOID
2445 NTAPI
2446 MiWriteProtectSystemImage(
2447     _In_ PVOID ImageBase)
2448 {
2449     PIMAGE_NT_HEADERS NtHeaders;
2450     PIMAGE_SECTION_HEADER SectionHeaders, Section;
2451     ULONG i;
2452     PVOID SectionBase, SectionEnd;
2453     ULONG SectionSize;
2454     ULONG Protection;
2455     PMMPTE FirstPte, LastPte;
2456 
2457     /* Check if the registry setting is on or not */
2458     if (MmEnforceWriteProtection == FALSE)
2459     {
2460         /* Ignore section protection */
2461         return;
2462     }
2463 
2464     /* Large page mapped images are not supported */
2465     NT_ASSERT(!MI_IS_PHYSICAL_ADDRESS(ImageBase));
2466 
2467     /* Session images are not yet supported */
2468     NT_ASSERT(!MI_IS_SESSION_ADDRESS(ImageBase));
2469 
2470     /* Get the NT headers */
2471     NtHeaders = RtlImageNtHeader(ImageBase);
2472     if (NtHeaders == NULL)
2473     {
2474         DPRINT1("Failed to get NT headers for image @ %p\n", ImageBase);
2475         return;
2476     }
2477 
2478     /* Don't touch NT4 drivers */
2479     if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) ||
2480         (NtHeaders->OptionalHeader.MajorSubsystemVersion < 5))
2481     {
2482         DPRINT1("Skipping NT 4 driver @ %p\n", ImageBase);
2483         return;
2484     }
2485 
2486     /* Get the section headers */
2487     SectionHeaders = IMAGE_FIRST_SECTION(NtHeaders);
2488 
2489     /* Get the base address of the first section */
2490     SectionBase = Add2Ptr(ImageBase, SectionHeaders[0].VirtualAddress);
2491 
2492     /* Start protecting the image header as R/O */
2493     FirstPte = MiAddressToPte(ImageBase);
2494     LastPte = MiAddressToPte(SectionBase) - 1;
2495     Protection = IMAGE_SCN_MEM_READ;
2496     if (LastPte >= FirstPte)
2497     {
2498         MiSetSystemCodeProtection(FirstPte, LastPte, IMAGE_SCN_MEM_READ);
2499     }
2500 
2501     /* Loop the sections */
2502     for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
2503     {
2504         /* Get the section base address and size */
2505         Section = &SectionHeaders[i];
2506         SectionBase = Add2Ptr(ImageBase, Section->VirtualAddress);
2507         SectionSize = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
2508 
2509         /* Get the first PTE of this section */
2510         FirstPte = MiAddressToPte(SectionBase);
2511 
2512         /* Check for overlap with the previous range */
2513         if (FirstPte == LastPte)
2514         {
2515             /* Combine the old and new protection by ORing them */
2516             Protection |= (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);
2517 
2518             /* Update the protection for this PTE */
2519             MiSetSystemCodeProtection(FirstPte, FirstPte, Protection);
2520 
2521             /* Skip this PTE */
2522             FirstPte++;
2523         }
2524 
2525         /* There can not be gaps! */
2526         NT_ASSERT(FirstPte == (LastPte + 1));
2527 
2528         /* Get the end of the section and the last PTE */
2529         SectionEnd = Add2Ptr(SectionBase, SectionSize - 1);
2530         NT_ASSERT(SectionEnd < Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage));
2531         LastPte = MiAddressToPte(SectionEnd);
2532 
2533         /* If there are no more pages (after an overlap), skip this section */
2534         if (LastPte < FirstPte)
2535         {
2536             NT_ASSERT(FirstPte == (LastPte + 1));
2537             continue;
2538         }
2539 
2540         /* Get the section protection */
2541         Protection = (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);
2542 
2543         /* Update the protection for this section */
2544         MiSetSystemCodeProtection(FirstPte, LastPte, Protection);
2545     }
2546 
2547     /* Image should end with the last section */
2548     if (ALIGN_UP_POINTER_BY(SectionEnd, PAGE_SIZE) !=
2549         Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage))
2550     {
2551         DPRINT1("ImageBase 0x%p ImageSize 0x%lx Section %u VA 0x%lx Raw 0x%lx virt 0x%lx\n",
2552             ImageBase,
2553             NtHeaders->OptionalHeader.SizeOfImage,
2554             i,
2555             Section->VirtualAddress,
2556             Section->SizeOfRawData,
2557             Section->Misc.VirtualSize);
2558     }
2559 }
2560 
2561 VOID
2562 NTAPI
2563 MiSetPagingOfDriver(IN PMMPTE PointerPte,
2564                     IN PMMPTE LastPte)
2565 {
2566     PVOID ImageBase;
2567     PETHREAD CurrentThread = PsGetCurrentThread();
2568     PFN_COUNT PageCount = 0;
2569     PFN_NUMBER PageFrameIndex;
2570     PMMPFN Pfn1;
2571     PAGED_CODE();
2572 
2573     /* The page fault handler is broken and doesn't page back in! */
2574     DPRINT1("WARNING: MiSetPagingOfDriver() called, but paging is broken! ignoring!\n");
2575     return;
2576 
2577     /* Get the driver's base address */
2578     ImageBase = MiPteToAddress(PointerPte);
2579     ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
2580 
2581     /* If this is a large page, it's stuck in physical memory */
2582     if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
2583 
2584     /* Lock the working set */
2585     MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
2586 
2587     /* Loop the PTEs */
2588     while (PointerPte <= LastPte)
2589     {
2590         /* Check for valid PTE */
2591         if (PointerPte->u.Hard.Valid == 1)
2592         {
2593             PageFrameIndex = PFN_FROM_PTE(PointerPte);
2594             Pfn1 = MiGetPfnEntry(PageFrameIndex);
2595             ASSERT(Pfn1->u2.ShareCount == 1);
2596 
2597             /* No working sets in ReactOS yet */
2598             PageCount++;
2599         }
2600 
2601         ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
2602         PointerPte++;
2603     }
2604 
2605     /* Release the working set */
2606     MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
2607 
2608     /* Do we have any driver pages? */
2609     if (PageCount)
2610     {
2611         /* Update counters */
2612         InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
2613     }
2614 }
2615 
2616 VOID
2617 NTAPI
2618 MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
2619 {
2620     ULONG_PTR ImageBase;
2621     PIMAGE_NT_HEADERS NtHeaders;
2622     ULONG Sections, Alignment, Size;
2623     PIMAGE_SECTION_HEADER Section;
2624     PMMPTE PointerPte = NULL, LastPte = NULL;
2625     if (MmDisablePagingExecutive) return;
2626 
2627     /* Get the driver base address and its NT header */
2628     ImageBase = (ULONG_PTR)LdrEntry->DllBase;
2629     NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
2630     if (!NtHeaders) return;
2631 
2632     /* Get the sections and their alignment */
2633     Sections = NtHeaders->FileHeader.NumberOfSections;
2634     Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
2635 
2636     /* Loop each section */
2637     Section = IMAGE_FIRST_SECTION(NtHeaders);
2638     while (Sections)
2639     {
2640         /* Find PAGE or .edata */
2641         if ((*(PULONG)Section->Name == 'EGAP') ||
2642             (*(PULONG)Section->Name == 'ade.'))
2643         {
2644             /* Had we already done some work? */
2645             if (!PointerPte)
2646             {
2647                 /* Nope, setup the first PTE address */
2648                 PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
2649                                                            Section->VirtualAddress));
2650             }
2651 
2652             /* Compute the size */
2653             Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
2654 
2655             /* Find the last PTE that maps this section */
2656             LastPte = MiAddressToPte(ImageBase +
2657                                      Section->VirtualAddress +
2658                                      Alignment + Size - PAGE_SIZE);
2659         }
2660         else
2661         {
2662             /* Had we found a section before? */
2663             if (PointerPte)
2664             {
2665                 /* Mark it as pageable */
2666                 MiSetPagingOfDriver(PointerPte, LastPte);
2667                 PointerPte = NULL;
2668             }
2669         }
2670 
2671         /* Keep searching */
2672         Sections--;
2673         Section++;
2674     }
2675 
2676     /* Handle the straggler */
2677     if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
2678 }
2679 
2680 BOOLEAN
2681 NTAPI
2682 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
2683 {
2684     PIMAGE_NT_HEADERS NtHeader;
2685     PAGED_CODE();
2686 
2687     /* Get NT Headers */
2688     NtHeader = RtlImageNtHeader(BaseAddress);
2689     if (NtHeader)
2690     {
2691         /* Check if this image is only safe for UP while we have 2+ CPUs */
2692         if ((KeNumberProcessors > 1) &&
2693             (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
2694         {
2695             /* Fail */
2696             return FALSE;
2697         }
2698     }
2699 
2700     /* Otherwise, it's safe */
2701     return TRUE;
2702 }
2703 
2704 NTSTATUS
2705 NTAPI
2706 MmCheckSystemImage(IN HANDLE ImageHandle,
2707                    IN BOOLEAN PurgeSection)
2708 {
2709     NTSTATUS Status;
2710     HANDLE SectionHandle;
2711     PVOID ViewBase = NULL;
2712     SIZE_T ViewSize = 0;
2713     IO_STATUS_BLOCK IoStatusBlock;
2714     FILE_STANDARD_INFORMATION FileStandardInfo;
2715     KAPC_STATE ApcState;
2716     PIMAGE_NT_HEADERS NtHeaders;
2717     OBJECT_ATTRIBUTES ObjectAttributes;
2718     PAGED_CODE();
2719 
2720     /* Setup the object attributes */
2721     InitializeObjectAttributes(&ObjectAttributes,
2722                                NULL,
2723                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2724                                NULL,
2725                                NULL);
2726 
2727     /* Create a section for the DLL */
2728     Status = ZwCreateSection(&SectionHandle,
2729                              SECTION_MAP_EXECUTE,
2730                              &ObjectAttributes,
2731                              NULL,
2732                              PAGE_EXECUTE,
2733                              SEC_IMAGE,
2734                              ImageHandle);
2735     if (!NT_SUCCESS(Status))
2736     {
2737         DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
2738         return Status;
2739     }
2740 
2741     /* Make sure we're in the system process */
2742     KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
2743 
2744     /* Map it */
2745     Status = ZwMapViewOfSection(SectionHandle,
2746                                 NtCurrentProcess(),
2747                                 &ViewBase,
2748                                 0,
2749                                 0,
2750                                 NULL,
2751                                 &ViewSize,
2752                                 ViewShare,
2753                                 0,
2754                                 PAGE_EXECUTE);
2755     if (!NT_SUCCESS(Status))
2756     {
2757         /* We failed, close the handle and return */
2758         DPRINT1("ZwMapViewOfSection failed with status 0x%x\n", Status);
2759         KeUnstackDetachProcess(&ApcState);
2760         ZwClose(SectionHandle);
2761         return Status;
2762     }
2763 
2764     /* Now query image information */
2765     Status = ZwQueryInformationFile(ImageHandle,
2766                                     &IoStatusBlock,
2767                                     &FileStandardInfo,
2768                                     sizeof(FileStandardInfo),
2769                                     FileStandardInformation);
2770     if (NT_SUCCESS(Status))
2771     {
2772         /* First, verify the checksum */
2773         if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
2774                                                  ViewSize,
2775                                                  FileStandardInfo.
2776                                                  EndOfFile.LowPart))
2777         {
2778             /* Set checksum failure */
2779             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
2780             goto Fail;
2781         }
2782 
2783         /* Make sure it's a real image */
2784         NtHeaders = RtlImageNtHeader(ViewBase);
2785         if (!NtHeaders)
2786         {
2787             /* Set checksum failure */
2788             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
2789             goto Fail;
2790         }
2791 
2792         /* Make sure it's for the correct architecture */
2793         if ((NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_NATIVE) ||
2794             (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC))
2795         {
2796             /* Set protection failure */
2797             Status = STATUS_INVALID_IMAGE_PROTECT;
2798             goto Fail;
2799         }
2800 
2801         /* Check that it's a valid SMP image if we have more then one CPU */
2802         if (!MmVerifyImageIsOkForMpUse(ViewBase))
2803         {
2804             /* Otherwise it's not the right image */
2805             Status = STATUS_IMAGE_MP_UP_MISMATCH;
2806         }
2807     }
2808 
2809     /* Unmap the section, close the handle, and return status */
2810 Fail:
2811     ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
2812     KeUnstackDetachProcess(&ApcState);
2813     ZwClose(SectionHandle);
2814     return Status;
2815 }
2816 
2817 NTSTATUS
2818 NTAPI
2819 MmLoadSystemImage(IN PUNICODE_STRING FileName,
2820                   IN PUNICODE_STRING NamePrefix OPTIONAL,
2821                   IN PUNICODE_STRING LoadedName OPTIONAL,
2822                   IN ULONG Flags,
2823                   OUT PVOID *ModuleObject,
2824                   OUT PVOID *ImageBaseAddress)
2825 {
2826     PVOID ModuleLoadBase = NULL;
2827     NTSTATUS Status;
2828     HANDLE FileHandle = NULL;
2829     OBJECT_ATTRIBUTES ObjectAttributes;
2830     IO_STATUS_BLOCK IoStatusBlock;
2831     PIMAGE_NT_HEADERS NtHeader;
2832     UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
2833     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
2834     ULONG EntrySize, DriverSize;
2835     PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
2836     PCHAR MissingApiName, Buffer;
2837     PWCHAR MissingDriverName;
2838     HANDLE SectionHandle;
2839     ACCESS_MASK DesiredAccess;
2840     PSECTION Section = NULL;
2841     BOOLEAN LockOwned = FALSE;
2842     PLIST_ENTRY NextEntry;
2843     IMAGE_INFO ImageInfo;
2844     STRING AnsiTemp;
2845     PAGED_CODE();
2846 
2847     /* Detect session-load */
2848     if (Flags)
2849     {
2850         /* Sanity checks */
2851         ASSERT(NamePrefix == NULL);
2852         ASSERT(LoadedName == NULL);
2853 
2854         /* Make sure the process is in session too */
2855         if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
2856     }
2857 
2858     /* Allocate a buffer we'll use for names */
2859     Buffer = ExAllocatePoolWithTag(NonPagedPool,
2860                                    MAXIMUM_FILENAME_LENGTH,
2861                                    TAG_LDR_WSTR);
2862     if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
2863 
2864     /* Check for a separator */
2865     if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
2866     {
2867         PWCHAR p;
2868         ULONG BaseLength;
2869 
2870         /* Loop the path until we get to the base name */
2871         p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
2872         while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
2873 
2874         /* Get the length */
2875         BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
2876         BaseLength *= sizeof(WCHAR);
2877 
2878         /* Setup the string */
2879         BaseName.Length = (USHORT)BaseLength;
2880         BaseName.Buffer = p;
2881     }
2882     else
2883     {
2884         /* Otherwise, we already have a base name */
2885         BaseName.Length = FileName->Length;
2886         BaseName.Buffer = FileName->Buffer;
2887     }
2888 
2889     /* Setup the maximum length */
2890     BaseName.MaximumLength = BaseName.Length;
2891 
2892     /* Now compute the base directory */
2893     BaseDirectory = *FileName;
2894     BaseDirectory.Length -= BaseName.Length;
2895     BaseDirectory.MaximumLength = BaseDirectory.Length;
2896 
2897     /* And the prefix, which for now is just the name itself */
2898     PrefixName = *FileName;
2899 
2900     /* Check if we have a prefix */
2901     if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
2902 
2903     /* Check if we already have a name, use it instead */
2904     if (LoadedName) BaseName = *LoadedName;
2905 
2906     /* Check for loader snap debugging */
2907     if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
2908     {
2909         /* Print out standard string */
2910         DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
2911                 &PrefixName, &BaseName, Flags ? "in session space" : "");
2912     }
2913 
2914     /* Acquire the load lock */
2915 LoaderScan:
2916     ASSERT(LockOwned == FALSE);
2917     LockOwned = TRUE;
2918     KeEnterCriticalRegion();
2919     KeWaitForSingleObject(&MmSystemLoadLock,
2920                           WrVirtualMemory,
2921                           KernelMode,
2922                           FALSE,
2923                           NULL);
2924 
2925     /* Scan the module list */
2926     NextEntry = PsLoadedModuleList.Flink;
2927     while (NextEntry != &PsLoadedModuleList)
2928     {
2929         /* Get the entry and compare the names */
2930         LdrEntry = CONTAINING_RECORD(NextEntry,
2931                                      LDR_DATA_TABLE_ENTRY,
2932                                      InLoadOrderLinks);
2933         if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
2934         {
2935             /* Found it, break out */
2936             break;
2937         }
2938 
2939         /* Keep scanning */
2940         NextEntry = NextEntry->Flink;
2941     }
2942 
2943     /* Check if we found the image */
2944     if (NextEntry != &PsLoadedModuleList)
2945     {
2946         /* Check if we had already mapped a section */
2947         if (Section)
2948         {
2949             /* Dereference and clear */
2950             ObDereferenceObject(Section);
2951             Section = NULL;
2952         }
2953 
2954         /* Check if this was supposed to be a session load */
2955         if (!Flags)
2956         {
2957             /* It wasn't, so just return the data */
2958             *ModuleObject = LdrEntry;
2959             *ImageBaseAddress = LdrEntry->DllBase;
2960             Status = STATUS_IMAGE_ALREADY_LOADED;
2961         }
2962         else
2963         {
2964             /* We don't support session loading yet */
2965             UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
2966             Status = STATUS_NOT_IMPLEMENTED;
2967         }
2968 
2969         /* Do cleanup */
2970         goto Quickie;
2971     }
2972     else if (!Section)
2973     {
2974         /* It wasn't loaded, and we didn't have a previous attempt */
2975         KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
2976         KeLeaveCriticalRegion();
2977         LockOwned = FALSE;
2978 
2979         /* Check if KD is enabled */
2980         if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
2981         {
2982             /* FIXME: Attempt to get image from KD */
2983         }
2984 
2985         /* We don't have a valid entry */
2986         LdrEntry = NULL;
2987 
2988         /* Setup image attributes */
2989         InitializeObjectAttributes(&ObjectAttributes,
2990                                    FileName,
2991                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2992                                    NULL,
2993                                    NULL);
2994 
2995         /* Open the image */
2996         Status = ZwOpenFile(&FileHandle,
2997                             FILE_EXECUTE,
2998                             &ObjectAttributes,
2999                             &IoStatusBlock,
3000                             FILE_SHARE_READ | FILE_SHARE_DELETE,
3001                             0);
3002         if (!NT_SUCCESS(Status))
3003         {
3004             DPRINT1("ZwOpenFile failed for '%wZ' with status 0x%x\n",
3005                     FileName, Status);
3006             goto Quickie;
3007         }
3008 
3009         /* Validate it */
3010         Status = MmCheckSystemImage(FileHandle, FALSE);
3011         if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
3012             (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
3013             (Status == STATUS_INVALID_IMAGE_PROTECT))
3014         {
3015             /* Fail loading */
3016             goto Quickie;
3017         }
3018 
3019         /* Check if this is a session-load */
3020         if (Flags)
3021         {
3022             /* Then we only need read and execute */
3023             DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
3024         }
3025         else
3026         {
3027             /* Otherwise, we can allow write access */
3028             DesiredAccess = SECTION_ALL_ACCESS;
3029         }
3030 
3031         /* Initialize the attributes for the section */
3032         InitializeObjectAttributes(&ObjectAttributes,
3033                                    NULL,
3034                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3035                                    NULL,
3036                                    NULL);
3037 
3038         /* Create the section */
3039         Status = ZwCreateSection(&SectionHandle,
3040                                  DesiredAccess,
3041                                  &ObjectAttributes,
3042                                  NULL,
3043                                  PAGE_EXECUTE,
3044                                  SEC_IMAGE,
3045                                  FileHandle);
3046         if (!NT_SUCCESS(Status))
3047         {
3048             DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
3049             goto Quickie;
3050         }
3051 
3052         /* Now get the section pointer */
3053         Status = ObReferenceObjectByHandle(SectionHandle,
3054                                            SECTION_MAP_EXECUTE,
3055                                            MmSectionObjectType,
3056                                            KernelMode,
3057                                            (PVOID*)&Section,
3058                                            NULL);
3059         ZwClose(SectionHandle);
3060         if (!NT_SUCCESS(Status)) goto Quickie;
3061 
3062         /* Check if this was supposed to be a session-load */
3063         if (Flags)
3064         {
3065             /* We don't support session loading yet */
3066             UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
3067             goto Quickie;
3068         }
3069 
3070         /* Check the loader list again, we should end up in the path below */
3071         goto LoaderScan;
3072     }
3073     else
3074     {
3075         /* We don't have a valid entry */
3076         LdrEntry = NULL;
3077     }
3078 
3079     /* Load the image */
3080     Status = MiLoadImageSection(&Section,
3081                                 &ModuleLoadBase,
3082                                 FileName,
3083                                 FALSE,
3084                                 NULL);
3085     ASSERT(Status != STATUS_ALREADY_COMMITTED);
3086 
3087     /* Get the size of the driver */
3088     DriverSize = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment)->ImageInformation.ImageFileSize;
3089 
3090     /* Make sure we're not being loaded into session space */
3091     if (!Flags)
3092     {
3093         /* Check for success */
3094         if (NT_SUCCESS(Status))
3095         {
3096             /* Support large pages for drivers */
3097             MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
3098                                  &ModuleLoadBase,
3099                                  &BaseName,
3100                                  TRUE);
3101         }
3102 
3103         /* Dereference the section */
3104         ObDereferenceObject(Section);
3105         Section = NULL;
3106     }
3107 
3108     /* Check for failure of the load earlier */
3109     if (!NT_SUCCESS(Status))
3110     {
3111         DPRINT1("MiLoadImageSection failed with status 0x%x\n", Status);
3112         goto Quickie;
3113     }
3114 
3115     /* Relocate the driver */
3116     Status = LdrRelocateImageWithBias(ModuleLoadBase,
3117                                       0,
3118                                       "SYSLDR",
3119                                       STATUS_SUCCESS,
3120                                       STATUS_CONFLICTING_ADDRESSES,
3121                                       STATUS_INVALID_IMAGE_FORMAT);
3122     if (!NT_SUCCESS(Status))
3123     {
3124         DPRINT1("LdrRelocateImageWithBias failed with status 0x%x\n", Status);
3125         goto Quickie;
3126     }
3127 
3128     /* Get the NT Header */
3129     NtHeader = RtlImageNtHeader(ModuleLoadBase);
3130 
3131     /* Calculate the size we'll need for the entry and allocate it */
3132     EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
3133                 BaseName.Length +
3134                 sizeof(UNICODE_NULL);
3135 
3136     /* Allocate the entry */
3137     LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
3138     if (!LdrEntry)
3139     {
3140         /* Fail */
3141         Status = STATUS_INSUFFICIENT_RESOURCES;
3142         goto Quickie;
3143     }
3144 
3145     /* Setup the entry */
3146     LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
3147     LdrEntry->LoadCount = 1;
3148     LdrEntry->LoadedImports = LoadedImports;
3149     LdrEntry->PatchInformation = NULL;
3150 
3151     /* Check the version */
3152     if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
3153         (NtHeader->OptionalHeader.MajorImageVersion >= 5))
3154     {
3155         /* Mark this image as a native image */
3156         LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
3157     }
3158 
3159     /* Setup the rest of the entry */
3160     LdrEntry->DllBase = ModuleLoadBase;
3161     LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
3162                                    NtHeader->OptionalHeader.AddressOfEntryPoint);
3163     LdrEntry->SizeOfImage = DriverSize;
3164     LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
3165     LdrEntry->SectionPointer = Section;
3166 
3167     /* Now write the DLL name */
3168     LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
3169     LdrEntry->BaseDllName.Length = BaseName.Length;
3170     LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
3171 
3172     /* Copy and null-terminate it */
3173     RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
3174                   BaseName.Buffer,
3175                   BaseName.Length);
3176     LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
3177 
3178     /* Now allocate the full name */
3179     LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
3180                                                          PrefixName.Length +
3181                                                          sizeof(UNICODE_NULL),
3182                                                          TAG_LDR_WSTR);
3183     if (!LdrEntry->FullDllName.Buffer)
3184     {
3185         /* Don't fail, just set it to zero */
3186         LdrEntry->FullDllName.Length = 0;
3187         LdrEntry->FullDllName.MaximumLength = 0;
3188     }
3189     else
3190     {
3191         /* Set it up */
3192         LdrEntry->FullDllName.Length = PrefixName.Length;
3193         LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
3194 
3195         /* Copy and null-terminate */
3196         RtlCopyMemory(LdrEntry->FullDllName.Buffer,
3197                       PrefixName.Buffer,
3198                       PrefixName.Length);
3199         LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
3200     }
3201 
3202     /* Add the entry */
3203     MiProcessLoaderEntry(LdrEntry, TRUE);
3204 
3205     /* Resolve imports */
3206     MissingApiName = Buffer;
3207     MissingDriverName = NULL;
3208     Status = MiResolveImageReferences(ModuleLoadBase,
3209                                       &BaseDirectory,
3210                                       NULL,
3211                                       &MissingApiName,
3212                                       &MissingDriverName,
3213                                       &LoadedImports);
3214     if (!NT_SUCCESS(Status))
3215     {
3216         BOOLEAN NeedToFreeString = FALSE;
3217 
3218         /* If the lowest bit is set to 1, this is a hint that we need to free */
3219         if (*(ULONG_PTR*)&MissingDriverName & 1)
3220         {
3221             NeedToFreeString = TRUE;
3222             *(ULONG_PTR*)&MissingDriverName &= ~1;
3223         }
3224 
3225         DPRINT1("MiResolveImageReferences failed with status 0x%x\n", Status);
3226         DPRINT1(" Missing driver '%ls', missing API '%s'\n",
3227                 MissingDriverName, MissingApiName);
3228 
3229         if (NeedToFreeString)
3230         {
3231             ExFreePoolWithTag(MissingDriverName, TAG_LDR_WSTR);
3232         }
3233 
3234         /* Fail */
3235         MiProcessLoaderEntry(LdrEntry, FALSE);
3236 
3237         /* Check if we need to free the name */
3238         if (LdrEntry->FullDllName.Buffer)
3239         {
3240             /* Free it */
3241             ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
3242         }
3243 
3244         /* Free the entry itself */
3245         ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
3246         LdrEntry = NULL;
3247         goto Quickie;
3248     }
3249 
3250     /* Update the loader entry */
3251     LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
3252                         LDRP_ENTRY_PROCESSED |
3253                         LDRP_MM_LOADED);
3254     LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
3255     LdrEntry->LoadedImports = LoadedImports;
3256 
3257     /* FIXME: Call driver verifier's loader function */
3258 
3259     /* Write-protect the system image */
3260     MiWriteProtectSystemImage(LdrEntry->DllBase);
3261 
3262     /* Check if notifications are enabled */
3263     if (PsImageNotifyEnabled)
3264     {
3265         /* Fill out the notification data */
3266         ImageInfo.Properties = 0;
3267         ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
3268         ImageInfo.SystemModeImage = TRUE;
3269         ImageInfo.ImageSize = LdrEntry->SizeOfImage;
3270         ImageInfo.ImageBase = LdrEntry->DllBase;
3271         ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
3272 
3273         /* Send the notification */
3274         PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
3275     }
3276 
3277 #ifdef __ROS_ROSSYM__
3278     /* MiCacheImageSymbols doesn't detect rossym */
3279     if (TRUE)
3280 #else
3281     /* Check if there's symbols */
3282     if (MiCacheImageSymbols(LdrEntry->DllBase))
3283 #endif
3284     {
3285         /* Check if the system root is present */
3286         if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
3287             !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
3288         {
3289             /* Add the system root */
3290             UnicodeTemp = PrefixName;
3291             UnicodeTemp.Buffer += 11;
3292             UnicodeTemp.Length -= (11 * sizeof(WCHAR));
3293             sprintf_nt(Buffer,
3294                        "%ws%wZ",
3295                        &SharedUserData->NtSystemRoot[2],
3296                        &UnicodeTemp);
3297         }
3298         else
3299         {
3300             /* Build the name */
3301             sprintf_nt(Buffer, "%wZ", &BaseName);
3302         }
3303 
3304         /* Setup the ansi string */
3305         RtlInitString(&AnsiTemp, Buffer);
3306 
3307         /* Notify the debugger */
3308         DbgLoadImageSymbols(&AnsiTemp,
3309                             LdrEntry->DllBase,
3310                             (ULONG_PTR)PsGetCurrentProcessId());
3311         LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
3312     }
3313 
3314     /* Page the driver */
3315     ASSERT(Section == NULL);
3316     MiEnablePagingOfDriver(LdrEntry);
3317 
3318     /* Return pointers */
3319     *ModuleObject = LdrEntry;
3320     *ImageBaseAddress = LdrEntry->DllBase;
3321 
3322 Quickie:
3323     /* Check if we have the lock acquired */
3324     if (LockOwned)
3325     {
3326         /* Release the lock */
3327         KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
3328         KeLeaveCriticalRegion();
3329         LockOwned = FALSE;
3330     }
3331 
3332     /* If we have a file handle, close it */
3333     if (FileHandle) ZwClose(FileHandle);
3334 
3335     /* Check if we had a prefix (not supported yet - PrefixName == *FileName now) */
3336     /* if (NamePrefix) ExFreePool(PrefixName.Buffer); */
3337 
3338     /* Free the name buffer and return status */
3339     ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
3340     return Status;
3341 }
3342 
3343 PLDR_DATA_TABLE_ENTRY
3344 NTAPI
3345 MiLookupDataTableEntry(IN PVOID Address)
3346 {
3347     PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
3348     PLIST_ENTRY NextEntry;
3349     PAGED_CODE();
3350 
3351     /* Loop entries */
3352     NextEntry = PsLoadedModuleList.Flink;
3353     do
3354     {
3355         /* Get the loader entry */
3356         LdrEntry =  CONTAINING_RECORD(NextEntry,
3357                                       LDR_DATA_TABLE_ENTRY,
3358                                       InLoadOrderLinks);
3359 
3360         /* Check if the address matches */
3361         if ((Address >= LdrEntry->DllBase) &&
3362             (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
3363                                LdrEntry->SizeOfImage)))
3364         {
3365             /* Found a match */
3366             FoundEntry = LdrEntry;
3367             break;
3368         }
3369 
3370         /* Move on */
3371         NextEntry = NextEntry->Flink;
3372     } while(NextEntry != &PsLoadedModuleList);
3373 
3374     /* Return the entry */
3375     return FoundEntry;
3376 }
3377 
3378 /* PUBLIC FUNCTIONS ***********************************************************/
3379 
3380 /*
3381  * @implemented
3382  */
3383 PVOID
3384 NTAPI
3385 MmPageEntireDriver(IN PVOID AddressWithinSection)
3386 {
3387     PMMPTE StartPte, EndPte;
3388     PLDR_DATA_TABLE_ENTRY LdrEntry;
3389     PAGED_CODE();
3390 
3391     /* Get the loader entry */
3392     LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
3393     if (!LdrEntry) return NULL;
3394 
3395     /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
3396     if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
3397     {
3398         /* Don't do anything, just return the base address */
3399         return LdrEntry->DllBase;
3400     }
3401 
3402     /* Wait for active DPCs to finish before we page out the driver */
3403     KeFlushQueuedDpcs();
3404 
3405     /* Get the PTE range for the whole driver image */
3406     StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
3407     EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
3408 
3409     /* Enable paging for the PTE range */
3410     ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
3411     MiSetPagingOfDriver(StartPte, EndPte);
3412 
3413     /* Return the base address */
3414     return LdrEntry->DllBase;
3415 }
3416 
3417 /*
3418  * @unimplemented
3419  */
3420 VOID
3421 NTAPI
3422 MmResetDriverPaging(IN PVOID AddressWithinSection)
3423 {
3424     UNIMPLEMENTED;
3425 }
3426 
3427 /*
3428  * @implemented
3429  */
3430 PVOID
3431 NTAPI
3432 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
3433 {
3434     PVOID ProcAddress = NULL;
3435     ANSI_STRING AnsiRoutineName;
3436     NTSTATUS Status;
3437     PLIST_ENTRY NextEntry;
3438     PLDR_DATA_TABLE_ENTRY LdrEntry;
3439     BOOLEAN Found = FALSE;
3440     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
3441     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
3442     ULONG Modules = 0;
3443 
3444     /* Convert routine to ansi name */
3445     Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
3446                                           SystemRoutineName,
3447                                           TRUE);
3448     if (!NT_SUCCESS(Status)) return NULL;
3449 
3450     /* Lock the list */
3451     KeEnterCriticalRegion();
3452     ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
3453 
3454     /* Loop the loaded module list */
3455     NextEntry = PsLoadedModuleList.Flink;
3456     while (NextEntry != &PsLoadedModuleList)
3457     {
3458         /* Get the entry */
3459         LdrEntry = CONTAINING_RECORD(NextEntry,
3460                                      LDR_DATA_TABLE_ENTRY,
3461                                      InLoadOrderLinks);
3462 
3463         /* Check if it's the kernel or HAL */
3464         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
3465         {
3466             /* Found it */
3467             Found = TRUE;
3468             Modules++;
3469         }
3470         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
3471         {
3472             /* Found it */
3473             Found = TRUE;
3474             Modules++;
3475         }
3476 
3477         /* Check if we found a valid binary */
3478         if (Found)
3479         {
3480             /* Find the procedure name */
3481             ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
3482                                                       &AnsiRoutineName);
3483 
3484             /* Break out if we found it or if we already tried both modules */
3485             if (ProcAddress) break;
3486             if (Modules == 2) break;
3487         }
3488 
3489         /* Keep looping */
3490         NextEntry = NextEntry->Flink;
3491     }
3492 
3493     /* Release the lock */
3494     ExReleaseResourceLite(&PsLoadedModuleResource);
3495     KeLeaveCriticalRegion();
3496 
3497     /* Free the string and return */
3498     RtlFreeAnsiString(&AnsiRoutineName);
3499     return ProcAddress;
3500 }
3501 
3502 /* EOF */
3503