xref: /reactos/ntoskrnl/mm/ARM3/sysldr.c (revision 8786e12d)
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 INIT_FUNCTION
590 VOID
591 NTAPI
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);
1439 
1440     /* Try to actually free them */
1441     PagesFreed = MiDeleteSystemPageableVm(PointerPte,
1442                                           PagesFreed,
1443                                           0,
1444                                           NULL);
1445 }
1446 
1447 INIT_FUNCTION
1448 VOID
1449 NTAPI
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, InitSection;
1459     BOOLEAN InitFound;
1460     DBG_UNREFERENCED_LOCAL_VARIABLE(InitSection);
1461 
1462     /* So we don't free our own code yet */
1463     InitCode = (ULONG_PTR)&MiFindInitializationCode;
1464 
1465     /* Assume failure */
1466     *StartVa = NULL;
1467 
1468     /* Enter a critical region while we loop the list */
1469     KeEnterCriticalRegion();
1470 
1471     /* Loop all loaded modules */
1472     NextEntry = PsLoadedModuleList.Flink;
1473     while (NextEntry != &PsLoadedModuleList)
1474     {
1475         /* Get the loader entry and its DLL base */
1476         LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1477         DllBase = (ULONG_PTR)LdrEntry->DllBase;
1478 
1479         /* Only process boot loaded images. Other drivers are processed by
1480            MmFreeDriverInitialization */
1481         if (LdrEntry->Flags & LDRP_MM_LOADED)
1482         {
1483             /* Keep going */
1484             NextEntry = NextEntry->Flink;
1485             continue;
1486         }
1487 
1488         /* Get the NT header */
1489         NtHeader = RtlImageNtHeader((PVOID)DllBase);
1490         if (!NtHeader)
1491         {
1492             /* Keep going */
1493             NextEntry = NextEntry->Flink;
1494             continue;
1495         }
1496 
1497         /* Get the first section, the section count, and scan them all */
1498         Section = IMAGE_FIRST_SECTION(NtHeader);
1499         SectionCount = NtHeader->FileHeader.NumberOfSections;
1500         InitStart = 0;
1501         while (SectionCount > 0)
1502         {
1503             /* Assume failure */
1504             InitFound = FALSE;
1505 
1506             /* Is this the INIT section or a discardable section? */
1507             if ((strncmp((PCCH)Section->Name, "INIT", 5) == 0) ||
1508                 ((Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)))
1509             {
1510                 /* Remember this */
1511                 InitFound = TRUE;
1512                 InitSection = Section;
1513             }
1514 
1515             if (InitFound)
1516             {
1517                 /* Pick the biggest size -- either raw or virtual */
1518                 Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
1519 
1520                 /* Read the section alignment */
1521                 Alignment = NtHeader->OptionalHeader.SectionAlignment;
1522 
1523                 /* Get the start and end addresses */
1524                 InitStart = DllBase + Section->VirtualAddress;
1525                 InitEnd = ALIGN_UP_BY(InitStart + Size, Alignment);
1526 
1527                 /* Align the addresses to PAGE_SIZE */
1528                 InitStart = ALIGN_UP_BY(InitStart, PAGE_SIZE);
1529                 InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
1530 
1531                 /* Have we reached the last section? */
1532                 if (SectionCount == 1)
1533                 {
1534                     /* Remember this */
1535                     LastSection = Section;
1536                 }
1537                 else
1538                 {
1539                     /* We have not, loop all the sections */
1540                     LastSection = NULL;
1541                     do
1542                     {
1543                         /* Keep going until we find a non-discardable section range */
1544                         SectionCount--;
1545                         Section++;
1546                         if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
1547                         {
1548                             /* Discardable, so record it, then keep going */
1549                             LastSection = Section;
1550                         }
1551                         else
1552                         {
1553                             /* Non-contigous discard flag, or no flag, break out */
1554                             break;
1555                         }
1556                     }
1557                     while (SectionCount > 1);
1558                 }
1559 
1560                 /* Have we found a discardable or init section? */
1561                 if (LastSection)
1562                 {
1563                     /* Pick the biggest size -- either raw or virtual */
1564                     Size = max(LastSection->SizeOfRawData, LastSection->Misc.VirtualSize);
1565 
1566                     /* Use this as the end of the section address */
1567                     InitEnd = DllBase + LastSection->VirtualAddress + Size;
1568 
1569                     /* Have we reached the last section yet? */
1570                     if (SectionCount != 1)
1571                     {
1572                         /* Then align this accross the session boundary */
1573                         InitEnd = ALIGN_UP_BY(InitEnd, Alignment);
1574                         InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
1575                     }
1576                 }
1577 
1578                 /* Make sure we don't let the init section go past the image */
1579                 ImageEnd = DllBase + LdrEntry->SizeOfImage;
1580                 if (InitEnd > ImageEnd) InitEnd = ALIGN_UP_BY(ImageEnd, PAGE_SIZE);
1581 
1582                 /* Make sure we have a valid, non-zero init section */
1583                 if (InitStart < InitEnd)
1584                 {
1585                     /* Make sure we are not within this code itself */
1586                     if ((InitCode >= InitStart) && (InitCode < InitEnd))
1587                     {
1588                         /* Return it, we can't free ourselves now */
1589                         ASSERT(*StartVa == 0);
1590                         *StartVa = (PVOID)InitStart;
1591                         *EndVa = (PVOID)InitEnd;
1592                     }
1593                     else
1594                     {
1595                         /* This isn't us -- go ahead and free it */
1596                         ASSERT(MI_IS_PHYSICAL_ADDRESS((PVOID)InitStart) == FALSE);
1597                         DPRINT("Freeing init code: %p-%p ('%wZ' @%p : '%s')\n",
1598                                (PVOID)InitStart,
1599                                (PVOID)InitEnd,
1600                                &LdrEntry->BaseDllName,
1601                                LdrEntry->DllBase,
1602                                InitSection->Name);
1603                         MiFreeInitializationCode((PVOID)InitStart, (PVOID)InitEnd);
1604                     }
1605                 }
1606             }
1607 
1608             /* Move to the next section */
1609             SectionCount--;
1610             Section++;
1611         }
1612 
1613         /* Move to the next module */
1614         NextEntry = NextEntry->Flink;
1615     }
1616 
1617     /* Leave the critical region and return */
1618     KeLeaveCriticalRegion();
1619 }
1620 
1621 /*
1622  * Note: This function assumes that all discardable sections are at the end of
1623  * the PE file. It searches backwards until it finds the non-discardable section
1624  */
1625 VOID
1626 NTAPI
1627 MmFreeDriverInitialization(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
1628 {
1629     PMMPTE StartPte, EndPte;
1630     PFN_NUMBER PageCount;
1631     PVOID DllBase;
1632     ULONG i;
1633     PIMAGE_NT_HEADERS NtHeader;
1634     PIMAGE_SECTION_HEADER Section, DiscardSection;
1635 
1636     /* Get the base address and the page count */
1637     DllBase = LdrEntry->DllBase;
1638     PageCount = LdrEntry->SizeOfImage >> PAGE_SHIFT;
1639 
1640     /* Get the last PTE in this image */
1641     EndPte = MiAddressToPte(DllBase) + PageCount;
1642 
1643     /* Get the NT header */
1644     NtHeader = RtlImageNtHeader(DllBase);
1645     if (!NtHeader) return;
1646 
1647     /* Get the last section and loop each section backwards */
1648     Section = IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections;
1649     DiscardSection = NULL;
1650     for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
1651     {
1652         /* Go back a section and check if it's discardable */
1653         Section--;
1654         if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
1655         {
1656             /* It is, select it for freeing */
1657             DiscardSection = Section;
1658         }
1659         else
1660         {
1661             /* No more discardable sections exist, bail out */
1662             break;
1663         }
1664     }
1665 
1666     /* Bail out if there's nothing to free */
1667     if (!DiscardSection) return;
1668 
1669     /* Push the DLL base to the first disacrable section, and get its PTE */
1670     DllBase = (PVOID)ROUND_TO_PAGES((ULONG_PTR)DllBase + DiscardSection->VirtualAddress);
1671     ASSERT(MI_IS_PHYSICAL_ADDRESS(DllBase) == FALSE);
1672     StartPte = MiAddressToPte(DllBase);
1673 
1674     /* Check how many pages to free total */
1675     PageCount = (PFN_NUMBER)(EndPte - StartPte);
1676     if (!PageCount) return;
1677 
1678     /* Delete this many PTEs */
1679     MiDeleteSystemPageableVm(StartPte, PageCount, 0, NULL);
1680 }
1681 
1682 INIT_FUNCTION
1683 VOID
1684 NTAPI
1685 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1686 {
1687     PLIST_ENTRY NextEntry;
1688     ULONG i = 0;
1689     PIMAGE_NT_HEADERS NtHeader;
1690     PLDR_DATA_TABLE_ENTRY LdrEntry;
1691     PIMAGE_FILE_HEADER FileHeader;
1692     BOOLEAN ValidRelocs;
1693     PIMAGE_DATA_DIRECTORY DataDirectory;
1694     PVOID DllBase, NewImageAddress;
1695     NTSTATUS Status;
1696     PMMPTE PointerPte, StartPte, LastPte;
1697     PFN_COUNT PteCount;
1698     PMMPFN Pfn1;
1699     MMPTE TempPte, OldPte;
1700 
1701     /* Loop driver list */
1702     for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1703          NextEntry != &LoaderBlock->LoadOrderListHead;
1704          NextEntry = NextEntry->Flink)
1705     {
1706         /* Get the loader entry and NT header */
1707         LdrEntry = CONTAINING_RECORD(NextEntry,
1708                                      LDR_DATA_TABLE_ENTRY,
1709                                      InLoadOrderLinks);
1710         NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1711 
1712         /* Debug info */
1713         DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1714                 LdrEntry->DllBase,
1715                 (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
1716                 &LdrEntry->FullDllName);
1717 
1718         /* Get the first PTE and the number of PTEs we'll need */
1719         PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
1720         PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
1721         LastPte = StartPte + PteCount;
1722 
1723 #if MI_TRACE_PFNS
1724         /* Loop the PTEs */
1725         while (PointerPte < LastPte)
1726         {
1727             ULONG len;
1728             ASSERT(PointerPte->u.Hard.Valid == 1);
1729             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1730             len = wcslen(LdrEntry->BaseDllName.Buffer) * sizeof(WCHAR);
1731             snprintf(Pfn1->ProcessName, min(16, len), "%S", LdrEntry->BaseDllName.Buffer);
1732             PointerPte++;
1733         }
1734 #endif
1735         /* Skip kernel and HAL */
1736         /* ROS HACK: Skip BOOTVID/KDCOM too */
1737         i++;
1738         if (i <= 4) continue;
1739 
1740         /* Skip non-drivers */
1741         if (!NtHeader) continue;
1742 
1743         /* Get the file header and make sure we can relocate */
1744         FileHeader = &NtHeader->FileHeader;
1745         if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
1746         if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
1747             IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
1748 
1749         /* Everything made sense until now, check the relocation section too */
1750         DataDirectory = &NtHeader->OptionalHeader.
1751                         DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1752         if (!DataDirectory->VirtualAddress)
1753         {
1754             /* We don't really have relocations */
1755             ValidRelocs = FALSE;
1756         }
1757         else
1758         {
1759             /* Make sure the size is valid */
1760             if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1761                 LdrEntry->SizeOfImage)
1762             {
1763                 /* They're not, skip */
1764                 continue;
1765             }
1766 
1767             /* We have relocations */
1768             ValidRelocs = TRUE;
1769         }
1770 
1771         /* Remember the original address */
1772         DllBase = LdrEntry->DllBase;
1773 
1774         /* Loop the PTEs */
1775         PointerPte = StartPte;
1776         while (PointerPte < LastPte)
1777         {
1778             /* Mark the page modified in the PFN database */
1779             ASSERT(PointerPte->u.Hard.Valid == 1);
1780             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1781             ASSERT(Pfn1->u3.e1.Rom == 0);
1782             Pfn1->u3.e1.Modified = TRUE;
1783 
1784             /* Next */
1785             PointerPte++;
1786         }
1787 
1788         /* Now reserve system PTEs for the image */
1789         PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
1790         if (!PointerPte)
1791         {
1792             /* Shouldn't happen */
1793             ERROR_FATAL("[Mm0]: Couldn't allocate driver section!\n");
1794             return;
1795         }
1796 
1797         /* This is the new virtual address for the module */
1798         LastPte = PointerPte + PteCount;
1799         NewImageAddress = MiPteToAddress(PointerPte);
1800 
1801         /* Sanity check */
1802         DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1803         ASSERT(ExpInitializationPhase == 0);
1804 
1805         /* Loop the new driver PTEs */
1806         TempPte = ValidKernelPte;
1807         while (PointerPte < LastPte)
1808         {
1809             /* Copy the old data */
1810             OldPte = *StartPte;
1811             ASSERT(OldPte.u.Hard.Valid == 1);
1812 
1813             /* Set page number from the loader's memory */
1814             TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;
1815 
1816             /* Write it */
1817             MI_WRITE_VALID_PTE(PointerPte, TempPte);
1818 
1819             /* Move on */
1820             PointerPte++;
1821             StartPte++;
1822         }
1823 
1824         /* Update position */
1825         PointerPte -= PteCount;
1826 
1827         /* Sanity check */
1828         ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1829 
1830         /* Set the image base to the address where the loader put it */
1831         NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1832 
1833         /* Check if we had relocations */
1834         if (ValidRelocs)
1835         {
1836             /* Relocate the image */
1837             Status = LdrRelocateImageWithBias(NewImageAddress,
1838                                               0,
1839                                               "SYSLDR",
1840                                               STATUS_SUCCESS,
1841                                               STATUS_CONFLICTING_ADDRESSES,
1842                                               STATUS_INVALID_IMAGE_FORMAT);
1843             if (!NT_SUCCESS(Status))
1844             {
1845                 /* This shouldn't happen */
1846                 ERROR_FATAL("Relocations failed!\n");
1847                 return;
1848             }
1849         }
1850 
1851         /* Update the loader entry */
1852         LdrEntry->DllBase = NewImageAddress;
1853 
1854         /* Update the thunks */
1855         DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1856         MiUpdateThunks(LoaderBlock,
1857                        DllBase,
1858                        NewImageAddress,
1859                        LdrEntry->SizeOfImage);
1860 
1861         /* Update the loader entry */
1862         LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
1863         LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1864                                 NtHeader->OptionalHeader.AddressOfEntryPoint);
1865         LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;
1866 
1867         /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
1868     }
1869 }
1870 
1871 INIT_FUNCTION
1872 NTSTATUS
1873 NTAPI
1874 MiBuildImportsForBootDrivers(VOID)
1875 {
1876     PLIST_ENTRY NextEntry, NextEntry2;
1877     PLDR_DATA_TABLE_ENTRY LdrEntry, KernelEntry, HalEntry, LdrEntry2, LastEntry;
1878     PLDR_DATA_TABLE_ENTRY* EntryArray;
1879     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
1880     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
1881     PLOAD_IMPORTS LoadedImports;
1882     ULONG LoadedImportsSize, ImportSize;
1883     PULONG_PTR ImageThunk;
1884     ULONG_PTR DllBase, DllEnd;
1885     ULONG Modules = 0, i, j = 0;
1886     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
1887 
1888     /* Initialize variables */
1889     KernelEntry = HalEntry = LastEntry = NULL;
1890 
1891     /* Loop the loaded module list... we are early enough that no lock is needed */
1892     NextEntry = PsLoadedModuleList.Flink;
1893     while (NextEntry != &PsLoadedModuleList)
1894     {
1895         /* Get the entry */
1896         LdrEntry = CONTAINING_RECORD(NextEntry,
1897                                      LDR_DATA_TABLE_ENTRY,
1898                                      InLoadOrderLinks);
1899 
1900         /* Check if it's the kernel or HAL */
1901         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
1902         {
1903             /* Found it */
1904             KernelEntry = LdrEntry;
1905         }
1906         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
1907         {
1908             /* Found it */
1909             HalEntry = LdrEntry;
1910         }
1911 
1912         /* Check if this is a driver DLL */
1913         if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
1914         {
1915             /* Check if this is the HAL or kernel */
1916             if ((LdrEntry == HalEntry) || (LdrEntry == KernelEntry))
1917             {
1918                 /* Add a reference */
1919                 LdrEntry->LoadCount = 1;
1920             }
1921             else
1922             {
1923                 /* No referencing needed */
1924                 LdrEntry->LoadCount = 0;
1925             }
1926         }
1927         else
1928         {
1929             /* Add a reference for all other modules as well */
1930             LdrEntry->LoadCount = 1;
1931         }
1932 
1933         /* Remember this came from the loader */
1934         LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
1935 
1936         /* Keep looping */
1937         NextEntry = NextEntry->Flink;
1938         Modules++;
1939     }
1940 
1941     /* We must have at least found the kernel and HAL */
1942     if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;
1943 
1944     /* Allocate the list */
1945     EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), TAG_LDR_IMPORTS);
1946     if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;
1947 
1948     /* Loop the loaded module list again */
1949     NextEntry = PsLoadedModuleList.Flink;
1950     while (NextEntry != &PsLoadedModuleList)
1951     {
1952         /* Get the entry */
1953         LdrEntry = CONTAINING_RECORD(NextEntry,
1954                                      LDR_DATA_TABLE_ENTRY,
1955                                      InLoadOrderLinks);
1956 #ifdef _WORKING_LOADER_
1957         /* Get its imports */
1958         ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1959                                                   TRUE,
1960                                                   IMAGE_DIRECTORY_ENTRY_IAT,
1961                                                   &ImportSize);
1962         if (!ImageThunk)
1963 #else
1964         /* Get its imports */
1965         ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1966                                                         TRUE,
1967                                                         IMAGE_DIRECTORY_ENTRY_IMPORT,
1968                                                         &ImportSize);
1969         if (!ImportDescriptor)
1970 #endif
1971         {
1972             /* None present */
1973             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
1974             NextEntry = NextEntry->Flink;
1975             continue;
1976         }
1977 
1978         /* Clear the list and count the number of IAT thunks */
1979         RtlZeroMemory(EntryArray, Modules * sizeof(PVOID));
1980 #ifdef _WORKING_LOADER_
1981         ImportSize /= sizeof(ULONG_PTR);
1982 
1983         /* Scan the thunks */
1984         for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
1985 #else
1986         DllBase = DllEnd = i = 0;
1987         while ((ImportDescriptor->Name) &&
1988                (ImportDescriptor->OriginalFirstThunk))
1989         {
1990             /* Get the image thunk */
1991             ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
1992                                  ImportDescriptor->FirstThunk);
1993             while (*ImageThunk)
1994 #endif
1995             {
1996             /* Do we already have an address? */
1997             if (DllBase)
1998             {
1999                 /* Is the thunk in the same address? */
2000                 if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
2001                 {
2002                     /* Skip it, we already have a reference for it */
2003                     ASSERT(EntryArray[j]);
2004                     ImageThunk++;
2005                     continue;
2006                 }
2007             }
2008 
2009             /* Loop the loaded module list to locate this address owner */
2010             j = 0;
2011             NextEntry2 = PsLoadedModuleList.Flink;
2012             while (NextEntry2 != &PsLoadedModuleList)
2013             {
2014                 /* Get the entry */
2015                 LdrEntry2 = CONTAINING_RECORD(NextEntry2,
2016                                               LDR_DATA_TABLE_ENTRY,
2017                                               InLoadOrderLinks);
2018 
2019                 /* Get the address range for this module */
2020                 DllBase = (ULONG_PTR)LdrEntry2->DllBase;
2021                 DllEnd = DllBase + LdrEntry2->SizeOfImage;
2022 
2023                 /* Check if this IAT entry matches it */
2024                 if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
2025                 {
2026                     /* Save it */
2027                     //DPRINT1("Found imported dll: %wZ\n", &LdrEntry2->BaseDllName);
2028                     EntryArray[j] = LdrEntry2;
2029                     break;
2030                 }
2031 
2032                 /* Keep searching */
2033                 NextEntry2 = NextEntry2->Flink;
2034                 j++;
2035             }
2036 
2037             /* Do we have a thunk outside the range? */
2038             if ((*ImageThunk < DllBase) || (*ImageThunk >= DllEnd))
2039             {
2040                 /* Could be 0... */
2041                 if (*ImageThunk)
2042                 {
2043                     /* Should not be happening */
2044                     ERROR_FATAL("Broken IAT entry for %p at %p (%lx)\n",
2045                                 LdrEntry, ImageThunk, *ImageThunk);
2046                 }
2047 
2048                 /* Reset if we hit this */
2049                 DllBase = 0;
2050             }
2051 #ifndef _WORKING_LOADER_
2052             ImageThunk++;
2053             }
2054 
2055             i++;
2056             ImportDescriptor++;
2057 #endif
2058         }
2059 
2060         /* Now scan how many imports we really have */
2061         for (i = 0, ImportSize = 0; i < Modules; i++)
2062         {
2063             /* Skip HAL and kernel */
2064             if ((EntryArray[i]) &&
2065                 (EntryArray[i] != HalEntry) &&
2066                 (EntryArray[i] != KernelEntry))
2067             {
2068                 /* A valid reference */
2069                 LastEntry = EntryArray[i];
2070                 ImportSize++;
2071             }
2072         }
2073 
2074         /* Do we have any imports after all? */
2075         if (!ImportSize)
2076         {
2077             /* No */
2078             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
2079         }
2080         else if (ImportSize == 1)
2081         {
2082             /* A single entry import */
2083             LdrEntry->LoadedImports = (PVOID)((ULONG_PTR)LastEntry | MM_SYSLDR_SINGLE_ENTRY);
2084             LastEntry->LoadCount++;
2085         }
2086         else
2087         {
2088             /* We need an import table */
2089             LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
2090             LoadedImports = ExAllocatePoolWithTag(PagedPool,
2091                                                   LoadedImportsSize,
2092                                                   TAG_LDR_IMPORTS);
2093             ASSERT(LoadedImports);
2094 
2095             /* Save the count */
2096             LoadedImports->Count = ImportSize;
2097 
2098             /* Now copy all imports */
2099             for (i = 0, j = 0; i < Modules; i++)
2100             {
2101                 /* Skip HAL and kernel */
2102                 if ((EntryArray[i]) &&
2103                     (EntryArray[i] != HalEntry) &&
2104                     (EntryArray[i] != KernelEntry))
2105                 {
2106                     /* A valid reference */
2107                     //DPRINT1("Found valid entry: %p\n", EntryArray[i]);
2108                     LoadedImports->Entry[j] = EntryArray[i];
2109                     EntryArray[i]->LoadCount++;
2110                     j++;
2111                 }
2112             }
2113 
2114             /* Should had as many entries as we expected */
2115             ASSERT(j == ImportSize);
2116             LdrEntry->LoadedImports = LoadedImports;
2117         }
2118 
2119         /* Next */
2120         NextEntry = NextEntry->Flink;
2121     }
2122 
2123     /* Free the initial array */
2124     ExFreePoolWithTag(EntryArray, TAG_LDR_IMPORTS);
2125 
2126     /* FIXME: Might not need to keep the HAL/Kernel imports around */
2127 
2128     /* Kernel and HAL are loaded at boot */
2129     KernelEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
2130     HalEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
2131 
2132     /* All worked well */
2133     return STATUS_SUCCESS;
2134 }
2135 
2136 INIT_FUNCTION
2137 VOID
2138 NTAPI
2139 MiLocateKernelSections(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
2140 {
2141     ULONG_PTR DllBase;
2142     PIMAGE_NT_HEADERS NtHeaders;
2143     PIMAGE_SECTION_HEADER SectionHeader;
2144     ULONG Sections, Size;
2145 
2146     /* Get the kernel section header */
2147     DllBase = (ULONG_PTR)LdrEntry->DllBase;
2148     NtHeaders = RtlImageNtHeader((PVOID)DllBase);
2149     SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
2150 
2151     /* Loop all the sections */
2152     Sections = NtHeaders->FileHeader.NumberOfSections;
2153     while (Sections)
2154     {
2155         /* Grab the size of the section */
2156         Size = max(SectionHeader->SizeOfRawData, SectionHeader->Misc.VirtualSize);
2157 
2158         /* Check for .RSRC section */
2159         if (*(PULONG)SectionHeader->Name == 'rsr.')
2160         {
2161             /* Remember the PTEs so we can modify them later */
2162             MiKernelResourceStartPte = MiAddressToPte(DllBase +
2163                                                       SectionHeader->VirtualAddress);
2164             MiKernelResourceEndPte = MiKernelResourceStartPte +
2165                                      BYTES_TO_PAGES(SectionHeader->VirtualAddress + Size);
2166         }
2167         else if (*(PULONG)SectionHeader->Name == 'LOOP')
2168         {
2169             /* POOLCODE vs. POOLMI */
2170             if (*(PULONG)&SectionHeader->Name[4] == 'EDOC')
2171             {
2172                 /* Found Ex* Pool code */
2173                 ExPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
2174                 ExPoolCodeEnd = ExPoolCodeStart + Size;
2175             }
2176             else if (*(PUSHORT)&SectionHeader->Name[4] == 'MI')
2177             {
2178                 /* Found Mm* Pool code */
2179                 MmPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
2180                 MmPoolCodeEnd = ExPoolCodeStart + Size;
2181             }
2182         }
2183         else if ((*(PULONG)SectionHeader->Name == 'YSIM') &&
2184                  (*(PULONG)&SectionHeader->Name[4] == 'ETPS'))
2185         {
2186             /* Found MISYSPTE (Mm System PTE code)*/
2187             MmPteCodeStart = DllBase + SectionHeader->VirtualAddress;
2188             MmPteCodeEnd = ExPoolCodeStart + Size;
2189         }
2190 
2191         /* Keep going */
2192         Sections--;
2193         SectionHeader++;
2194     }
2195 }
2196 
2197 INIT_FUNCTION
2198 BOOLEAN
2199 NTAPI
2200 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
2201 {
2202     PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
2203     PLIST_ENTRY ListHead, NextEntry;
2204     ULONG EntrySize;
2205 
2206     /* Setup the loaded module list and locks */
2207     ExInitializeResourceLite(&PsLoadedModuleResource);
2208     KeInitializeSpinLock(&PsLoadedModuleSpinLock);
2209     InitializeListHead(&PsLoadedModuleList);
2210 
2211     /* Get loop variables and the kernel entry */
2212     ListHead = &LoaderBlock->LoadOrderListHead;
2213     NextEntry = ListHead->Flink;
2214     LdrEntry = CONTAINING_RECORD(NextEntry,
2215                                  LDR_DATA_TABLE_ENTRY,
2216                                  InLoadOrderLinks);
2217     PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
2218 
2219     /* Locate resource section, pool code, and system pte code */
2220     MiLocateKernelSections(LdrEntry);
2221 
2222     /* Loop the loader block */
2223     while (NextEntry != ListHead)
2224     {
2225         /* Get the loader entry */
2226         LdrEntry = CONTAINING_RECORD(NextEntry,
2227                                      LDR_DATA_TABLE_ENTRY,
2228                                      InLoadOrderLinks);
2229 
2230         /* FIXME: ROS HACK. Make sure this is a driver */
2231         if (!RtlImageNtHeader(LdrEntry->DllBase))
2232         {
2233             /* Skip this entry */
2234             NextEntry = NextEntry->Flink;
2235             continue;
2236         }
2237 
2238         /* Calculate the size we'll need and allocate a copy */
2239         EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
2240                     LdrEntry->BaseDllName.MaximumLength +
2241                     sizeof(UNICODE_NULL);
2242         NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
2243         if (!NewEntry) return FALSE;
2244 
2245         /* Copy the entry over */
2246         *NewEntry = *LdrEntry;
2247 
2248         /* Allocate the name */
2249         NewEntry->FullDllName.Buffer =
2250             ExAllocatePoolWithTag(PagedPool,
2251                                   LdrEntry->FullDllName.MaximumLength +
2252                                       sizeof(UNICODE_NULL),
2253                                   TAG_LDR_WSTR);
2254         if (!NewEntry->FullDllName.Buffer)
2255         {
2256             ExFreePoolWithTag(NewEntry, TAG_MODULE_OBJECT);
2257             return FALSE;
2258         }
2259 
2260         /* Set the base name */
2261         NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
2262 
2263         /* Copy the full and base name */
2264         RtlCopyMemory(NewEntry->FullDllName.Buffer,
2265                       LdrEntry->FullDllName.Buffer,
2266                       LdrEntry->FullDllName.MaximumLength);
2267         RtlCopyMemory(NewEntry->BaseDllName.Buffer,
2268                       LdrEntry->BaseDllName.Buffer,
2269                       LdrEntry->BaseDllName.MaximumLength);
2270 
2271         /* Null-terminate the base name */
2272         NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
2273                                      sizeof(WCHAR)] = UNICODE_NULL;
2274 
2275         /* Insert the entry into the list */
2276         InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
2277         NextEntry = NextEntry->Flink;
2278     }
2279 
2280     /* Build the import lists for the boot drivers */
2281     MiBuildImportsForBootDrivers();
2282 
2283     /* We're done */
2284     return TRUE;
2285 }
2286 
2287 LOGICAL
2288 NTAPI
2289 MiUseLargeDriverPage(IN ULONG NumberOfPtes,
2290                      IN OUT PVOID *ImageBaseAddress,
2291                      IN PUNICODE_STRING BaseImageName,
2292                      IN BOOLEAN BootDriver)
2293 {
2294     PLIST_ENTRY NextEntry;
2295     BOOLEAN DriverFound = FALSE;
2296     PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
2297     ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
2298     ASSERT(*ImageBaseAddress >= MmSystemRangeStart);
2299 
2300 #ifdef _X86_
2301     if (!(KeFeatureBits & KF_LARGE_PAGE)) return FALSE;
2302     if (!(__readcr4() & CR4_PSE)) return FALSE;
2303 #endif
2304 
2305     /* Make sure there's enough system PTEs for a large page driver */
2306     if (MmTotalFreeSystemPtes[SystemPteSpace] < (16 * (PDE_MAPPED_VA >> PAGE_SHIFT)))
2307     {
2308         return FALSE;
2309     }
2310 
2311     /* This happens if the registry key had a "*" (wildcard) in it */
2312     if (MiLargePageAllDrivers == 0)
2313     {
2314         /* It didn't, so scan the list */
2315         NextEntry = MiLargePageDriverList.Flink;
2316         while (NextEntry != &MiLargePageDriverList)
2317         {
2318             /* Check if the driver name matches */
2319             LargePageDriverEntry = CONTAINING_RECORD(NextEntry,
2320                                                      MI_LARGE_PAGE_DRIVER_ENTRY,
2321                                                      Links);
2322             if (RtlEqualUnicodeString(BaseImageName,
2323                                       &LargePageDriverEntry->BaseName,
2324                                       TRUE))
2325             {
2326                 /* Enable large pages for this driver */
2327                 DriverFound = TRUE;
2328                 break;
2329             }
2330 
2331             /* Keep trying */
2332             NextEntry = NextEntry->Flink;
2333         }
2334 
2335         /* If we didn't find the driver, it doesn't need large pages */
2336         if (DriverFound == FALSE) return FALSE;
2337     }
2338 
2339     /* Nothing to do yet */
2340     DPRINT1("Large pages not supported!\n");
2341     return FALSE;
2342 }
2343 
2344 VOID
2345 NTAPI
2346 MiSetSystemCodeProtection(
2347     _In_ PMMPTE FirstPte,
2348     _In_ PMMPTE LastPte,
2349     _In_ ULONG Protection)
2350 {
2351     PMMPTE PointerPte;
2352     MMPTE TempPte;
2353 
2354     /* Loop the PTEs */
2355     for (PointerPte = FirstPte; PointerPte <= LastPte; PointerPte++)
2356     {
2357         /* Read the PTE */
2358         TempPte = *PointerPte;
2359 
2360         /* Make sure it's valid */
2361         ASSERT(TempPte.u.Hard.Valid == 1);
2362 
2363         /* Update the protection */
2364         TempPte.u.Hard.Write = BooleanFlagOn(Protection, IMAGE_SCN_MEM_WRITE);
2365 #if _MI_HAS_NO_EXECUTE
2366         TempPte.u.Hard.NoExecute = !BooleanFlagOn(Protection, IMAGE_SCN_MEM_EXECUTE);
2367 #endif
2368 
2369         MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2370     }
2371 
2372     /* Flush it all */
2373     KeFlushEntireTb(TRUE, TRUE);
2374 
2375     return;
2376 }
2377 
2378 VOID
2379 NTAPI
2380 MiWriteProtectSystemImage(
2381     _In_ PVOID ImageBase)
2382 {
2383     PIMAGE_NT_HEADERS NtHeaders;
2384     PIMAGE_SECTION_HEADER SectionHeaders, Section;
2385     ULONG i;
2386     PVOID SectionBase, SectionEnd;
2387     ULONG SectionSize;
2388     ULONG Protection;
2389     PMMPTE FirstPte, LastPte;
2390 
2391     /* Check if the registry setting is on or not */
2392     if (MmEnforceWriteProtection)
2393     {
2394         /* Ignore section protection */
2395         return;
2396     }
2397 
2398     /* Large page mapped images are not supported */
2399     NT_ASSERT(!MI_IS_PHYSICAL_ADDRESS(ImageBase));
2400 
2401     /* Session images are not yet supported */
2402     NT_ASSERT(!MI_IS_SESSION_ADDRESS(ImageBase));
2403 
2404     /* Get the NT headers */
2405     NtHeaders = RtlImageNtHeader(ImageBase);
2406     if (NtHeaders == NULL)
2407     {
2408         DPRINT1("Failed to get NT headers for image @ %p\n", ImageBase);
2409         return;
2410     }
2411 
2412     /* Don't touch NT4 drivers */
2413     if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) ||
2414         (NtHeaders->OptionalHeader.MajorSubsystemVersion < 5))
2415     {
2416         DPRINT1("Skipping NT 4 driver @ %p\n", ImageBase);
2417         return;
2418     }
2419 
2420     /* Get the section headers */
2421     SectionHeaders = IMAGE_FIRST_SECTION(NtHeaders);
2422 
2423     /* Get the base address of the first section */
2424     SectionBase = Add2Ptr(ImageBase, SectionHeaders[0].VirtualAddress);
2425 
2426     /* Start protecting the image header as R/O */
2427     FirstPte = MiAddressToPte(ImageBase);
2428     LastPte = MiAddressToPte(SectionBase) - 1;
2429     Protection = IMAGE_SCN_MEM_READ;
2430     if (LastPte >= FirstPte)
2431     {
2432         MiSetSystemCodeProtection(FirstPte, LastPte, IMAGE_SCN_MEM_READ);
2433     }
2434 
2435     /* Loop the sections */
2436     for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
2437     {
2438         /* Get the section base address and size */
2439         Section = &SectionHeaders[i];
2440         SectionBase = Add2Ptr(ImageBase, Section->VirtualAddress);
2441         SectionSize = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
2442 
2443         /* Get the first PTE of this section */
2444         FirstPte = MiAddressToPte(SectionBase);
2445 
2446         /* Check for overlap with the previous range */
2447         if (FirstPte == LastPte)
2448         {
2449             /* Combine the old and new protection by ORing them */
2450             Protection |= (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);
2451 
2452             /* Update the protection for this PTE */
2453             MiSetSystemCodeProtection(FirstPte, FirstPte, Protection);
2454 
2455             /* Skip this PTE */
2456             FirstPte++;
2457         }
2458 
2459         /* There can not be gaps! */
2460         NT_ASSERT(FirstPte == (LastPte + 1));
2461 
2462         /* Get the end of the section and the last PTE */
2463         SectionEnd = Add2Ptr(SectionBase, SectionSize - 1);
2464         NT_ASSERT(SectionEnd < Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage));
2465         LastPte = MiAddressToPte(SectionEnd);
2466 
2467         /* If there are no more pages (after an overlap), skip this section */
2468         if (LastPte < FirstPte)
2469         {
2470             NT_ASSERT(FirstPte == (LastPte + 1));
2471             continue;
2472         }
2473 
2474         /* Get the section protection */
2475         Protection = (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);
2476 
2477         /* Update the protection for this section */
2478         MiSetSystemCodeProtection(FirstPte, LastPte, Protection);
2479     }
2480 
2481     /* Image should end with the last section */
2482     if (ALIGN_UP_POINTER_BY(SectionEnd, PAGE_SIZE) !=
2483         Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage))
2484     {
2485         DPRINT1("ImageBase 0x%p ImageSize 0x%lx Section %u VA 0x%lx Raw 0x%lx virt 0x%lx\n",
2486             ImageBase,
2487             NtHeaders->OptionalHeader.SizeOfImage,
2488             i,
2489             Section->VirtualAddress,
2490             Section->SizeOfRawData,
2491             Section->Misc.VirtualSize);
2492     }
2493 }
2494 
2495 VOID
2496 NTAPI
2497 MiSetPagingOfDriver(IN PMMPTE PointerPte,
2498                     IN PMMPTE LastPte)
2499 {
2500     PVOID ImageBase;
2501     PETHREAD CurrentThread = PsGetCurrentThread();
2502     PFN_COUNT PageCount = 0;
2503     PFN_NUMBER PageFrameIndex;
2504     PMMPFN Pfn1;
2505     PAGED_CODE();
2506 
2507     /* The page fault handler is broken and doesn't page back in! */
2508     DPRINT1("WARNING: MiSetPagingOfDriver() called, but paging is broken! ignoring!\n");
2509     return;
2510 
2511     /* Get the driver's base address */
2512     ImageBase = MiPteToAddress(PointerPte);
2513     ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
2514 
2515     /* If this is a large page, it's stuck in physical memory */
2516     if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
2517 
2518     /* Lock the working set */
2519     MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
2520 
2521     /* Loop the PTEs */
2522     while (PointerPte <= LastPte)
2523     {
2524         /* Check for valid PTE */
2525         if (PointerPte->u.Hard.Valid == 1)
2526         {
2527             PageFrameIndex = PFN_FROM_PTE(PointerPte);
2528             Pfn1 = MiGetPfnEntry(PageFrameIndex);
2529             ASSERT(Pfn1->u2.ShareCount == 1);
2530 
2531             /* No working sets in ReactOS yet */
2532             PageCount++;
2533         }
2534 
2535         ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
2536         PointerPte++;
2537     }
2538 
2539     /* Release the working set */
2540     MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
2541 
2542     /* Do we have any driver pages? */
2543     if (PageCount)
2544     {
2545         /* Update counters */
2546         InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
2547     }
2548 }
2549 
2550 VOID
2551 NTAPI
2552 MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
2553 {
2554     ULONG_PTR ImageBase;
2555     PIMAGE_NT_HEADERS NtHeaders;
2556     ULONG Sections, Alignment, Size;
2557     PIMAGE_SECTION_HEADER Section;
2558     PMMPTE PointerPte = NULL, LastPte = NULL;
2559     if (MmDisablePagingExecutive) return;
2560 
2561     /* Get the driver base address and its NT header */
2562     ImageBase = (ULONG_PTR)LdrEntry->DllBase;
2563     NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
2564     if (!NtHeaders) return;
2565 
2566     /* Get the sections and their alignment */
2567     Sections = NtHeaders->FileHeader.NumberOfSections;
2568     Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
2569 
2570     /* Loop each section */
2571     Section = IMAGE_FIRST_SECTION(NtHeaders);
2572     while (Sections)
2573     {
2574         /* Find PAGE or .edata */
2575         if ((*(PULONG)Section->Name == 'EGAP') ||
2576             (*(PULONG)Section->Name == 'ade.'))
2577         {
2578             /* Had we already done some work? */
2579             if (!PointerPte)
2580             {
2581                 /* Nope, setup the first PTE address */
2582                 PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
2583                                                            Section->
2584                                                            VirtualAddress));
2585             }
2586 
2587             /* Compute the size */
2588             Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
2589 
2590             /* Find the last PTE that maps this section */
2591             LastPte = MiAddressToPte(ImageBase +
2592                                      Section->VirtualAddress +
2593                                      Alignment +
2594                                      Size -
2595                                      PAGE_SIZE);
2596         }
2597         else
2598         {
2599             /* Had we found a section before? */
2600             if (PointerPte)
2601             {
2602                 /* Mark it as pageable */
2603                 MiSetPagingOfDriver(PointerPte, LastPte);
2604                 PointerPte = NULL;
2605             }
2606         }
2607 
2608         /* Keep searching */
2609         Sections--;
2610         Section++;
2611     }
2612 
2613     /* Handle the straggler */
2614     if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
2615 }
2616 
2617 BOOLEAN
2618 NTAPI
2619 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
2620 {
2621     PIMAGE_NT_HEADERS NtHeader;
2622     PAGED_CODE();
2623 
2624     /* Get NT Headers */
2625     NtHeader = RtlImageNtHeader(BaseAddress);
2626     if (NtHeader)
2627     {
2628         /* Check if this image is only safe for UP while we have 2+ CPUs */
2629         if ((KeNumberProcessors > 1) &&
2630             (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
2631         {
2632             /* Fail */
2633             return FALSE;
2634         }
2635     }
2636 
2637     /* Otherwise, it's safe */
2638     return TRUE;
2639 }
2640 
2641 NTSTATUS
2642 NTAPI
2643 MmCheckSystemImage(IN HANDLE ImageHandle,
2644                    IN BOOLEAN PurgeSection)
2645 {
2646     NTSTATUS Status;
2647     HANDLE SectionHandle;
2648     PVOID ViewBase = NULL;
2649     SIZE_T ViewSize = 0;
2650     IO_STATUS_BLOCK IoStatusBlock;
2651     FILE_STANDARD_INFORMATION FileStandardInfo;
2652     KAPC_STATE ApcState;
2653     PIMAGE_NT_HEADERS NtHeaders;
2654     OBJECT_ATTRIBUTES ObjectAttributes;
2655     PAGED_CODE();
2656 
2657     /* Setup the object attributes */
2658     InitializeObjectAttributes(&ObjectAttributes,
2659                                NULL,
2660                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2661                                NULL,
2662                                NULL);
2663 
2664     /* Create a section for the DLL */
2665     Status = ZwCreateSection(&SectionHandle,
2666                              SECTION_MAP_EXECUTE,
2667                              &ObjectAttributes,
2668                              NULL,
2669                              PAGE_EXECUTE,
2670                              SEC_IMAGE,
2671                              ImageHandle);
2672     if (!NT_SUCCESS(Status))
2673     {
2674         DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
2675         return Status;
2676     }
2677 
2678     /* Make sure we're in the system process */
2679     KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
2680 
2681     /* Map it */
2682     Status = ZwMapViewOfSection(SectionHandle,
2683                                 NtCurrentProcess(),
2684                                 &ViewBase,
2685                                 0,
2686                                 0,
2687                                 NULL,
2688                                 &ViewSize,
2689                                 ViewShare,
2690                                 0,
2691                                 PAGE_EXECUTE);
2692     if (!NT_SUCCESS(Status))
2693     {
2694         /* We failed, close the handle and return */
2695         DPRINT1("ZwMapViewOfSection failed with status 0x%x\n", Status);
2696         KeUnstackDetachProcess(&ApcState);
2697         ZwClose(SectionHandle);
2698         return Status;
2699     }
2700 
2701     /* Now query image information */
2702     Status = ZwQueryInformationFile(ImageHandle,
2703                                     &IoStatusBlock,
2704                                     &FileStandardInfo,
2705                                     sizeof(FileStandardInfo),
2706                                     FileStandardInformation);
2707     if (NT_SUCCESS(Status))
2708     {
2709         /* First, verify the checksum */
2710         if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
2711                                                  ViewSize,
2712                                                  FileStandardInfo.
2713                                                  EndOfFile.LowPart))
2714         {
2715             /* Set checksum failure */
2716             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
2717             goto Fail;
2718         }
2719 
2720         /* Make sure it's a real image */
2721         NtHeaders = RtlImageNtHeader(ViewBase);
2722         if (!NtHeaders)
2723         {
2724             /* Set checksum failure */
2725             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
2726             goto Fail;
2727         }
2728 
2729         /* Make sure it's for the correct architecture */
2730         if ((NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_NATIVE) ||
2731             (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC))
2732         {
2733             /* Set protection failure */
2734             Status = STATUS_INVALID_IMAGE_PROTECT;
2735             goto Fail;
2736         }
2737 
2738         /* Check that it's a valid SMP image if we have more then one CPU */
2739         if (!MmVerifyImageIsOkForMpUse(ViewBase))
2740         {
2741             /* Otherwise it's not the right image */
2742             Status = STATUS_IMAGE_MP_UP_MISMATCH;
2743         }
2744     }
2745 
2746     /* Unmap the section, close the handle, and return status */
2747 Fail:
2748     ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
2749     KeUnstackDetachProcess(&ApcState);
2750     ZwClose(SectionHandle);
2751     return Status;
2752 }
2753 
2754 NTSTATUS
2755 NTAPI
2756 MmLoadSystemImage(IN PUNICODE_STRING FileName,
2757                   IN PUNICODE_STRING NamePrefix OPTIONAL,
2758                   IN PUNICODE_STRING LoadedName OPTIONAL,
2759                   IN ULONG Flags,
2760                   OUT PVOID *ModuleObject,
2761                   OUT PVOID *ImageBaseAddress)
2762 {
2763     PVOID ModuleLoadBase = NULL;
2764     NTSTATUS Status;
2765     HANDLE FileHandle = NULL;
2766     OBJECT_ATTRIBUTES ObjectAttributes;
2767     IO_STATUS_BLOCK IoStatusBlock;
2768     PIMAGE_NT_HEADERS NtHeader;
2769     UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
2770     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
2771     ULONG EntrySize, DriverSize;
2772     PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
2773     PCHAR MissingApiName, Buffer;
2774     PWCHAR MissingDriverName;
2775     HANDLE SectionHandle;
2776     ACCESS_MASK DesiredAccess;
2777     PVOID Section = NULL;
2778     BOOLEAN LockOwned = FALSE;
2779     PLIST_ENTRY NextEntry;
2780     IMAGE_INFO ImageInfo;
2781     STRING AnsiTemp;
2782     PAGED_CODE();
2783 
2784     /* Detect session-load */
2785     if (Flags)
2786     {
2787         /* Sanity checks */
2788         ASSERT(NamePrefix == NULL);
2789         ASSERT(LoadedName == NULL);
2790 
2791         /* Make sure the process is in session too */
2792         if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
2793     }
2794 
2795     /* Allocate a buffer we'll use for names */
2796     Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
2797     if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
2798 
2799     /* Check for a separator */
2800     if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
2801     {
2802         PWCHAR p;
2803         ULONG BaseLength;
2804 
2805         /* Loop the path until we get to the base name */
2806         p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
2807         while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
2808 
2809         /* Get the length */
2810         BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
2811         BaseLength *= sizeof(WCHAR);
2812 
2813         /* Setup the string */
2814         BaseName.Length = (USHORT)BaseLength;
2815         BaseName.Buffer = p;
2816     }
2817     else
2818     {
2819         /* Otherwise, we already have a base name */
2820         BaseName.Length = FileName->Length;
2821         BaseName.Buffer = FileName->Buffer;
2822     }
2823 
2824     /* Setup the maximum length */
2825     BaseName.MaximumLength = BaseName.Length;
2826 
2827     /* Now compute the base directory */
2828     BaseDirectory = *FileName;
2829     BaseDirectory.Length -= BaseName.Length;
2830     BaseDirectory.MaximumLength = BaseDirectory.Length;
2831 
2832     /* And the prefix, which for now is just the name itself */
2833     PrefixName = *FileName;
2834 
2835     /* Check if we have a prefix */
2836     if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
2837 
2838     /* Check if we already have a name, use it instead */
2839     if (LoadedName) BaseName = *LoadedName;
2840 
2841     /* Check for loader snap debugging */
2842     if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
2843     {
2844         /* Print out standard string */
2845         DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
2846                 &PrefixName, &BaseName, Flags ? "in session space" : "");
2847     }
2848 
2849     /* Acquire the load lock */
2850 LoaderScan:
2851     ASSERT(LockOwned == FALSE);
2852     LockOwned = TRUE;
2853     KeEnterCriticalRegion();
2854     KeWaitForSingleObject(&MmSystemLoadLock,
2855                           WrVirtualMemory,
2856                           KernelMode,
2857                           FALSE,
2858                           NULL);
2859 
2860     /* Scan the module list */
2861     NextEntry = PsLoadedModuleList.Flink;
2862     while (NextEntry != &PsLoadedModuleList)
2863     {
2864         /* Get the entry and compare the names */
2865         LdrEntry = CONTAINING_RECORD(NextEntry,
2866                                      LDR_DATA_TABLE_ENTRY,
2867                                      InLoadOrderLinks);
2868         if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
2869         {
2870             /* Found it, break out */
2871             break;
2872         }
2873 
2874         /* Keep scanning */
2875         NextEntry = NextEntry->Flink;
2876     }
2877 
2878     /* Check if we found the image */
2879     if (NextEntry != &PsLoadedModuleList)
2880     {
2881         /* Check if we had already mapped a section */
2882         if (Section)
2883         {
2884             /* Dereference and clear */
2885             ObDereferenceObject(Section);
2886             Section = NULL;
2887         }
2888 
2889         /* Check if this was supposed to be a session load */
2890         if (!Flags)
2891         {
2892             /* It wasn't, so just return the data */
2893             *ModuleObject = LdrEntry;
2894             *ImageBaseAddress = LdrEntry->DllBase;
2895             Status = STATUS_IMAGE_ALREADY_LOADED;
2896         }
2897         else
2898         {
2899             /* We don't support session loading yet */
2900             UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
2901             Status = STATUS_NOT_IMPLEMENTED;
2902         }
2903 
2904         /* Do cleanup */
2905         goto Quickie;
2906     }
2907     else if (!Section)
2908     {
2909         /* It wasn't loaded, and we didn't have a previous attempt */
2910         KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
2911         KeLeaveCriticalRegion();
2912         LockOwned = FALSE;
2913 
2914         /* Check if KD is enabled */
2915         if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
2916         {
2917             /* FIXME: Attempt to get image from KD */
2918         }
2919 
2920         /* We don't have a valid entry */
2921         LdrEntry = NULL;
2922 
2923         /* Setup image attributes */
2924         InitializeObjectAttributes(&ObjectAttributes,
2925                                    FileName,
2926                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2927                                    NULL,
2928                                    NULL);
2929 
2930         /* Open the image */
2931         Status = ZwOpenFile(&FileHandle,
2932                             FILE_EXECUTE,
2933                             &ObjectAttributes,
2934                             &IoStatusBlock,
2935                             FILE_SHARE_READ | FILE_SHARE_DELETE,
2936                             0);
2937         if (!NT_SUCCESS(Status))
2938         {
2939             DPRINT1("ZwOpenFile failed for '%wZ' with status 0x%x\n",
2940                     FileName, Status);
2941             goto Quickie;
2942         }
2943 
2944         /* Validate it */
2945         Status = MmCheckSystemImage(FileHandle, FALSE);
2946         if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
2947             (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
2948             (Status == STATUS_INVALID_IMAGE_PROTECT))
2949         {
2950             /* Fail loading */
2951             goto Quickie;
2952         }
2953 
2954         /* Check if this is a session-load */
2955         if (Flags)
2956         {
2957             /* Then we only need read and execute */
2958             DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
2959         }
2960         else
2961         {
2962             /* Otherwise, we can allow write access */
2963             DesiredAccess = SECTION_ALL_ACCESS;
2964         }
2965 
2966         /* Initialize the attributes for the section */
2967         InitializeObjectAttributes(&ObjectAttributes,
2968                                    NULL,
2969                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2970                                    NULL,
2971                                    NULL);
2972 
2973         /* Create the section */
2974         Status = ZwCreateSection(&SectionHandle,
2975                                  DesiredAccess,
2976                                  &ObjectAttributes,
2977                                  NULL,
2978                                  PAGE_EXECUTE,
2979                                  SEC_IMAGE,
2980                                  FileHandle);
2981         if (!NT_SUCCESS(Status))
2982         {
2983             DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
2984             goto Quickie;
2985         }
2986 
2987         /* Now get the section pointer */
2988         Status = ObReferenceObjectByHandle(SectionHandle,
2989                                            SECTION_MAP_EXECUTE,
2990                                            MmSectionObjectType,
2991                                            KernelMode,
2992                                            &Section,
2993                                            NULL);
2994         ZwClose(SectionHandle);
2995         if (!NT_SUCCESS(Status)) goto Quickie;
2996 
2997         /* Check if this was supposed to be a session-load */
2998         if (Flags)
2999         {
3000             /* We don't support session loading yet */
3001             UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
3002             goto Quickie;
3003         }
3004 
3005         /* Check the loader list again, we should end up in the path below */
3006         goto LoaderScan;
3007     }
3008     else
3009     {
3010         /* We don't have a valid entry */
3011         LdrEntry = NULL;
3012     }
3013 
3014     /* Load the image */
3015     Status = MiLoadImageSection(&Section,
3016                                 &ModuleLoadBase,
3017                                 FileName,
3018                                 FALSE,
3019                                 NULL);
3020     ASSERT(Status != STATUS_ALREADY_COMMITTED);
3021 
3022     /* Get the size of the driver */
3023     DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageInformation.ImageFileSize;
3024 
3025     /* Make sure we're not being loaded into session space */
3026     if (!Flags)
3027     {
3028         /* Check for success */
3029         if (NT_SUCCESS(Status))
3030         {
3031             /* Support large pages for drivers */
3032             MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
3033                                  &ModuleLoadBase,
3034                                  &BaseName,
3035                                  TRUE);
3036         }
3037 
3038         /* Dereference the section */
3039         ObDereferenceObject(Section);
3040         Section = NULL;
3041     }
3042 
3043     /* Check for failure of the load earlier */
3044     if (!NT_SUCCESS(Status))
3045     {
3046         DPRINT1("MiLoadImageSection failed with status 0x%x\n", Status);
3047         goto Quickie;
3048     }
3049 
3050     /* Relocate the driver */
3051     Status = LdrRelocateImageWithBias(ModuleLoadBase,
3052                                       0,
3053                                       "SYSLDR",
3054                                       STATUS_SUCCESS,
3055                                       STATUS_CONFLICTING_ADDRESSES,
3056                                       STATUS_INVALID_IMAGE_FORMAT);
3057     if (!NT_SUCCESS(Status))
3058     {
3059         DPRINT1("LdrRelocateImageWithBias failed with status 0x%x\n", Status);
3060         goto Quickie;
3061     }
3062 
3063     /* Get the NT Header */
3064     NtHeader = RtlImageNtHeader(ModuleLoadBase);
3065 
3066     /* Calculate the size we'll need for the entry and allocate it */
3067     EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
3068                 BaseName.Length +
3069                 sizeof(UNICODE_NULL);
3070 
3071     /* Allocate the entry */
3072     LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
3073     if (!LdrEntry)
3074     {
3075         /* Fail */
3076         Status = STATUS_INSUFFICIENT_RESOURCES;
3077         goto Quickie;
3078     }
3079 
3080     /* Setup the entry */
3081     LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
3082     LdrEntry->LoadCount = 1;
3083     LdrEntry->LoadedImports = LoadedImports;
3084     LdrEntry->PatchInformation = NULL;
3085 
3086     /* Check the version */
3087     if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
3088         (NtHeader->OptionalHeader.MajorImageVersion >= 5))
3089     {
3090         /* Mark this image as a native image */
3091         LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
3092     }
3093 
3094     /* Setup the rest of the entry */
3095     LdrEntry->DllBase = ModuleLoadBase;
3096     LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
3097                                    NtHeader->OptionalHeader.AddressOfEntryPoint);
3098     LdrEntry->SizeOfImage = DriverSize;
3099     LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
3100     LdrEntry->SectionPointer = Section;
3101 
3102     /* Now write the DLL name */
3103     LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
3104     LdrEntry->BaseDllName.Length = BaseName.Length;
3105     LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
3106 
3107     /* Copy and null-terminate it */
3108     RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
3109                   BaseName.Buffer,
3110                   BaseName.Length);
3111     LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
3112 
3113     /* Now allocate the full name */
3114     LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
3115                                                          PrefixName.Length +
3116                                                          sizeof(UNICODE_NULL),
3117                                                          TAG_LDR_WSTR);
3118     if (!LdrEntry->FullDllName.Buffer)
3119     {
3120         /* Don't fail, just set it to zero */
3121         LdrEntry->FullDllName.Length = 0;
3122         LdrEntry->FullDllName.MaximumLength = 0;
3123     }
3124     else
3125     {
3126         /* Set it up */
3127         LdrEntry->FullDllName.Length = PrefixName.Length;
3128         LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
3129 
3130         /* Copy and null-terminate */
3131         RtlCopyMemory(LdrEntry->FullDllName.Buffer,
3132                       PrefixName.Buffer,
3133                       PrefixName.Length);
3134         LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
3135     }
3136 
3137     /* Add the entry */
3138     MiProcessLoaderEntry(LdrEntry, TRUE);
3139 
3140     /* Resolve imports */
3141     MissingApiName = Buffer;
3142     MissingDriverName = NULL;
3143     Status = MiResolveImageReferences(ModuleLoadBase,
3144                                       &BaseDirectory,
3145                                       NULL,
3146                                       &MissingApiName,
3147                                       &MissingDriverName,
3148                                       &LoadedImports);
3149     if (!NT_SUCCESS(Status))
3150     {
3151         BOOLEAN NeedToFreeString = FALSE;
3152 
3153         /* If the lowest bit is set to 1, this is a hint that we need to free */
3154         if (*(ULONG_PTR*)&MissingDriverName & 1)
3155         {
3156             NeedToFreeString = TRUE;
3157             *(ULONG_PTR*)&MissingDriverName &= ~1;
3158         }
3159 
3160         DPRINT1("MiResolveImageReferences failed with status 0x%x\n", Status);
3161         DPRINT1(" Missing driver '%ls', missing API '%s'\n",
3162                 MissingDriverName, MissingApiName);
3163 
3164         if (NeedToFreeString)
3165         {
3166             ExFreePoolWithTag(MissingDriverName, TAG_LDR_WSTR);
3167         }
3168 
3169         /* Fail */
3170         MiProcessLoaderEntry(LdrEntry, FALSE);
3171 
3172         /* Check if we need to free the name */
3173         if (LdrEntry->FullDllName.Buffer)
3174         {
3175             /* Free it */
3176             ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
3177         }
3178 
3179         /* Free the entry itself */
3180         ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
3181         LdrEntry = NULL;
3182         goto Quickie;
3183     }
3184 
3185     /* Update the loader entry */
3186     LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
3187                         LDRP_ENTRY_PROCESSED |
3188                         LDRP_MM_LOADED);
3189     LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
3190     LdrEntry->LoadedImports = LoadedImports;
3191 
3192     /* FIXME: Call driver verifier's loader function */
3193 
3194     /* Write-protect the system image */
3195     MiWriteProtectSystemImage(LdrEntry->DllBase);
3196 
3197     /* Check if notifications are enabled */
3198     if (PsImageNotifyEnabled)
3199     {
3200         /* Fill out the notification data */
3201         ImageInfo.Properties = 0;
3202         ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
3203         ImageInfo.SystemModeImage = TRUE;
3204         ImageInfo.ImageSize = LdrEntry->SizeOfImage;
3205         ImageInfo.ImageBase = LdrEntry->DllBase;
3206         ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
3207 
3208         /* Send the notification */
3209         PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
3210     }
3211 
3212 #if defined(KDBG) || defined(_WINKD_)
3213     /* MiCacheImageSymbols doesn't detect rossym */
3214     if (TRUE)
3215 #else
3216     /* Check if there's symbols */
3217     if (MiCacheImageSymbols(LdrEntry->DllBase))
3218 #endif
3219     {
3220         /* Check if the system root is present */
3221         if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
3222             !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
3223         {
3224             /* Add the system root */
3225             UnicodeTemp = PrefixName;
3226             UnicodeTemp.Buffer += 11;
3227             UnicodeTemp.Length -= (11 * sizeof(WCHAR));
3228             sprintf_nt(Buffer,
3229                        "%ws%wZ",
3230                        &SharedUserData->NtSystemRoot[2],
3231                        &UnicodeTemp);
3232         }
3233         else
3234         {
3235             /* Build the name */
3236             sprintf_nt(Buffer, "%wZ", &BaseName);
3237         }
3238 
3239         /* Setup the ansi string */
3240         RtlInitString(&AnsiTemp, Buffer);
3241 
3242         /* Notify the debugger */
3243         DbgLoadImageSymbols(&AnsiTemp,
3244                             LdrEntry->DllBase,
3245                             (ULONG_PTR)PsGetCurrentProcessId());
3246         LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
3247     }
3248 
3249     /* Page the driver */
3250     ASSERT(Section == NULL);
3251     MiEnablePagingOfDriver(LdrEntry);
3252 
3253     /* Return pointers */
3254     *ModuleObject = LdrEntry;
3255     *ImageBaseAddress = LdrEntry->DllBase;
3256 
3257 Quickie:
3258     /* Check if we have the lock acquired */
3259     if (LockOwned)
3260     {
3261         /* Release the lock */
3262         KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
3263         KeLeaveCriticalRegion();
3264         LockOwned = FALSE;
3265     }
3266 
3267     /* If we have a file handle, close it */
3268     if (FileHandle) ZwClose(FileHandle);
3269 
3270     /* Check if we had a prefix (not supported yet - PrefixName == *FileName now) */
3271     /* if (NamePrefix) ExFreePool(PrefixName.Buffer); */
3272 
3273     /* Free the name buffer and return status */
3274     ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
3275     return Status;
3276 }
3277 
3278 PLDR_DATA_TABLE_ENTRY
3279 NTAPI
3280 MiLookupDataTableEntry(IN PVOID Address)
3281 {
3282     PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
3283     PLIST_ENTRY NextEntry;
3284     PAGED_CODE();
3285 
3286     /* Loop entries */
3287     NextEntry = PsLoadedModuleList.Flink;
3288     do
3289     {
3290         /* Get the loader entry */
3291         LdrEntry =  CONTAINING_RECORD(NextEntry,
3292                                       LDR_DATA_TABLE_ENTRY,
3293                                       InLoadOrderLinks);
3294 
3295         /* Check if the address matches */
3296         if ((Address >= LdrEntry->DllBase) &&
3297             (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
3298                                LdrEntry->SizeOfImage)))
3299         {
3300             /* Found a match */
3301             FoundEntry = LdrEntry;
3302             break;
3303         }
3304 
3305         /* Move on */
3306         NextEntry = NextEntry->Flink;
3307     } while(NextEntry != &PsLoadedModuleList);
3308 
3309     /* Return the entry */
3310     return FoundEntry;
3311 }
3312 
3313 /* PUBLIC FUNCTIONS ***********************************************************/
3314 
3315 /*
3316  * @implemented
3317  */
3318 PVOID
3319 NTAPI
3320 MmPageEntireDriver(IN PVOID AddressWithinSection)
3321 {
3322     PMMPTE StartPte, EndPte;
3323     PLDR_DATA_TABLE_ENTRY LdrEntry;
3324     PAGED_CODE();
3325 
3326     /* Get the loader entry */
3327     LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
3328     if (!LdrEntry) return NULL;
3329 
3330     /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
3331     if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
3332     {
3333         /* Don't do anything, just return the base address */
3334         return LdrEntry->DllBase;
3335     }
3336 
3337     /* Wait for active DPCs to finish before we page out the driver */
3338     KeFlushQueuedDpcs();
3339 
3340     /* Get the PTE range for the whole driver image */
3341     StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
3342     EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
3343 
3344     /* Enable paging for the PTE range */
3345     ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
3346     MiSetPagingOfDriver(StartPte, EndPte);
3347 
3348     /* Return the base address */
3349     return LdrEntry->DllBase;
3350 }
3351 
3352 /*
3353  * @unimplemented
3354  */
3355 VOID
3356 NTAPI
3357 MmResetDriverPaging(IN PVOID AddressWithinSection)
3358 {
3359     UNIMPLEMENTED;
3360 }
3361 
3362 /*
3363  * @implemented
3364  */
3365 PVOID
3366 NTAPI
3367 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
3368 {
3369     PVOID ProcAddress = NULL;
3370     ANSI_STRING AnsiRoutineName;
3371     NTSTATUS Status;
3372     PLIST_ENTRY NextEntry;
3373     PLDR_DATA_TABLE_ENTRY LdrEntry;
3374     BOOLEAN Found = FALSE;
3375     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
3376     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
3377     ULONG Modules = 0;
3378 
3379     /* Convert routine to ansi name */
3380     Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
3381                                           SystemRoutineName,
3382                                           TRUE);
3383     if (!NT_SUCCESS(Status)) return NULL;
3384 
3385     /* Lock the list */
3386     KeEnterCriticalRegion();
3387     ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
3388 
3389     /* Loop the loaded module list */
3390     NextEntry = PsLoadedModuleList.Flink;
3391     while (NextEntry != &PsLoadedModuleList)
3392     {
3393         /* Get the entry */
3394         LdrEntry = CONTAINING_RECORD(NextEntry,
3395                                      LDR_DATA_TABLE_ENTRY,
3396                                      InLoadOrderLinks);
3397 
3398         /* Check if it's the kernel or HAL */
3399         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
3400         {
3401             /* Found it */
3402             Found = TRUE;
3403             Modules++;
3404         }
3405         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
3406         {
3407             /* Found it */
3408             Found = TRUE;
3409             Modules++;
3410         }
3411 
3412         /* Check if we found a valid binary */
3413         if (Found)
3414         {
3415             /* Find the procedure name */
3416             ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
3417                                                       &AnsiRoutineName);
3418 
3419             /* Break out if we found it or if we already tried both modules */
3420             if (ProcAddress) break;
3421             if (Modules == 2) break;
3422         }
3423 
3424         /* Keep looping */
3425         NextEntry = NextEntry->Flink;
3426     }
3427 
3428     /* Release the lock */
3429     ExReleaseResourceLite(&PsLoadedModuleResource);
3430     KeLeaveCriticalRegion();
3431 
3432     /* Free the string and return */
3433     RtlFreeAnsiString(&AnsiRoutineName);
3434     return ProcAddress;
3435 }
3436 
3437 /* EOF */
3438