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