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