xref: /reactos/ntoskrnl/mm/ARM3/section.c (revision b8dd046e)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/section.c
5  * PURPOSE:         ARM Memory Manager Section Support
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
17 
18 /* GLOBALS ********************************************************************/
19 
20 ACCESS_MASK MmMakeSectionAccess[8] =
21 {
22     SECTION_MAP_READ,
23     SECTION_MAP_READ,
24     SECTION_MAP_EXECUTE,
25     SECTION_MAP_EXECUTE | SECTION_MAP_READ,
26     SECTION_MAP_WRITE,
27     SECTION_MAP_READ,
28     SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
29     SECTION_MAP_EXECUTE | SECTION_MAP_READ
30 };
31 
32 ACCESS_MASK MmMakeFileAccess[8] =
33 {
34     FILE_READ_DATA,
35     FILE_READ_DATA,
36     FILE_EXECUTE,
37     FILE_EXECUTE | FILE_READ_DATA,
38     FILE_WRITE_DATA | FILE_READ_DATA,
39     FILE_READ_DATA,
40     FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA,
41     FILE_EXECUTE | FILE_READ_DATA
42 };
43 
44 CHAR MmUserProtectionToMask1[16] =
45 {
46     0,
47     MM_NOACCESS,
48     MM_READONLY,
49     (CHAR)MM_INVALID_PROTECTION,
50     MM_READWRITE,
51     (CHAR)MM_INVALID_PROTECTION,
52     (CHAR)MM_INVALID_PROTECTION,
53     (CHAR)MM_INVALID_PROTECTION,
54     MM_WRITECOPY,
55     (CHAR)MM_INVALID_PROTECTION,
56     (CHAR)MM_INVALID_PROTECTION,
57     (CHAR)MM_INVALID_PROTECTION,
58     (CHAR)MM_INVALID_PROTECTION,
59     (CHAR)MM_INVALID_PROTECTION,
60     (CHAR)MM_INVALID_PROTECTION,
61     (CHAR)MM_INVALID_PROTECTION
62 };
63 
64 CHAR MmUserProtectionToMask2[16] =
65 {
66     0,
67     MM_EXECUTE,
68     MM_EXECUTE_READ,
69     (CHAR)MM_INVALID_PROTECTION,
70     MM_EXECUTE_READWRITE,
71     (CHAR)MM_INVALID_PROTECTION,
72     (CHAR)MM_INVALID_PROTECTION,
73     (CHAR)MM_INVALID_PROTECTION,
74     MM_EXECUTE_WRITECOPY,
75     (CHAR)MM_INVALID_PROTECTION,
76     (CHAR)MM_INVALID_PROTECTION,
77     (CHAR)MM_INVALID_PROTECTION,
78     (CHAR)MM_INVALID_PROTECTION,
79     (CHAR)MM_INVALID_PROTECTION,
80     (CHAR)MM_INVALID_PROTECTION,
81     (CHAR)MM_INVALID_PROTECTION
82 };
83 
84 ULONG MmCompatibleProtectionMask[8] =
85 {
86     PAGE_NOACCESS,
87 
88     PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
89 
90     PAGE_NOACCESS | PAGE_EXECUTE,
91 
92     PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
93     PAGE_EXECUTE_READ,
94 
95     PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE,
96 
97     PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
98 
99     PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE |
100     PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE |
101     PAGE_EXECUTE_WRITECOPY,
102 
103     PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
104     PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
105 };
106 
107 MMSESSION MmSession;
108 KGUARDED_MUTEX MmSectionCommitMutex;
109 MM_AVL_TABLE MmSectionBasedRoot;
110 KGUARDED_MUTEX MmSectionBasedMutex;
111 PVOID MmHighSectionBase;
112 
113 /* PRIVATE FUNCTIONS **********************************************************/
114 
115 BOOLEAN
116 NTAPI
117 MiIsProtectionCompatible(IN ULONG SectionPageProtection,
118                          IN ULONG NewSectionPageProtection)
119 {
120     ULONG ProtectionMask, CompatibleMask;
121 
122     /* Calculate the protection mask and make sure it's valid */
123     ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
124     if (ProtectionMask == MM_INVALID_PROTECTION)
125     {
126         DPRINT1("Invalid protection mask\n");
127         return FALSE;
128     }
129 
130     /* Calculate the compatible mask */
131     CompatibleMask = MmCompatibleProtectionMask[ProtectionMask & 0x7] |
132                      PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE;
133 
134     /* See if the mapping protection is compatible with the create protection */
135     return ((CompatibleMask | NewSectionPageProtection) == CompatibleMask);
136 }
137 
138 ACCESS_MASK
139 NTAPI
140 MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection)
141 {
142     ULONG ProtectionMask;
143 
144     /* Calculate the protection mask and make sure it's valid */
145     ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
146     if (ProtectionMask == MM_INVALID_PROTECTION)
147     {
148         DPRINT1("Invalid protection mask\n");
149         return STATUS_INVALID_PAGE_PROTECTION;
150     }
151 
152     /* Now convert it to the required file access */
153     return MmMakeFileAccess[ProtectionMask & 0x7];
154 }
155 
156 ULONG
157 NTAPI
158 MiMakeProtectionMask(IN ULONG Protect)
159 {
160     ULONG Mask1, Mask2, ProtectMask;
161 
162     /* PAGE_EXECUTE_WRITECOMBINE is theoretically the maximum */
163     if (Protect >= (PAGE_WRITECOMBINE * 2)) return MM_INVALID_PROTECTION;
164 
165     /*
166      * Windows API protection mask can be understood as two bitfields, differing
167      * by whether or not execute rights are being requested
168      */
169     Mask1 = Protect & 0xF;
170     Mask2 = (Protect >> 4) & 0xF;
171 
172     /* Check which field is there */
173     if (!Mask1)
174     {
175         /* Mask2 must be there, use it to determine the PTE protection */
176         if (!Mask2) return MM_INVALID_PROTECTION;
177         ProtectMask = MmUserProtectionToMask2[Mask2];
178     }
179     else
180     {
181         /* Mask2 should not be there, use Mask1 to determine the PTE mask */
182         if (Mask2) return MM_INVALID_PROTECTION;
183         ProtectMask = MmUserProtectionToMask1[Mask1];
184     }
185 
186     /* Make sure the final mask is a valid one */
187     if (ProtectMask == MM_INVALID_PROTECTION) return MM_INVALID_PROTECTION;
188 
189     /* Check for PAGE_GUARD option */
190     if (Protect & PAGE_GUARD)
191     {
192         /* It's not valid on no-access, nocache, or writecombine pages */
193         if ((ProtectMask == MM_NOACCESS) ||
194             (Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)))
195         {
196             /* Fail such requests */
197             return MM_INVALID_PROTECTION;
198         }
199 
200         /* This actually turns on guard page in this scenario! */
201         ProtectMask |= MM_GUARDPAGE;
202     }
203 
204     /* Check for nocache option */
205     if (Protect & PAGE_NOCACHE)
206     {
207         /* The earlier check should've eliminated this possibility */
208         ASSERT((Protect & PAGE_GUARD) == 0);
209 
210         /* Check for no-access page or write combine page */
211         if ((ProtectMask == MM_NOACCESS) || (Protect & PAGE_WRITECOMBINE))
212         {
213             /* Such a request is invalid */
214             return MM_INVALID_PROTECTION;
215         }
216 
217         /* Add the PTE flag */
218         ProtectMask |= MM_NOCACHE;
219     }
220 
221     /* Check for write combine option */
222     if (Protect & PAGE_WRITECOMBINE)
223     {
224         /* The two earlier scenarios should've caught this */
225         ASSERT((Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0);
226 
227         /* Don't allow on no-access pages */
228         if (ProtectMask == MM_NOACCESS) return MM_INVALID_PROTECTION;
229 
230         /* This actually turns on write-combine in this scenario! */
231         ProtectMask |= MM_NOACCESS;
232     }
233 
234     /* Return the final MM PTE protection mask */
235     return ProtectMask;
236 }
237 
238 BOOLEAN
239 NTAPI
240 MiInitializeSystemSpaceMap(IN PMMSESSION InputSession OPTIONAL)
241 {
242     SIZE_T AllocSize, BitmapSize, Size;
243     PVOID ViewStart;
244     PMMSESSION Session;
245 
246     /* Check if this a session or system space */
247     if (InputSession)
248     {
249         /* Use the input session */
250         Session = InputSession;
251         ViewStart = MiSessionViewStart;
252         Size = MmSessionViewSize;
253     }
254     else
255     {
256         /* Use the system space "session" */
257         Session = &MmSession;
258         ViewStart = MiSystemViewStart;
259         Size = MmSystemViewSize;
260     }
261 
262     /* Initialize the system space lock */
263     Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock;
264     KeInitializeGuardedMutex(Session->SystemSpaceViewLockPointer);
265 
266     /* Set the start address */
267     Session->SystemSpaceViewStart = ViewStart;
268 
269     /* Create a bitmap to describe system space */
270     BitmapSize = sizeof(RTL_BITMAP) + ((((Size / MI_SYSTEM_VIEW_BUCKET_SIZE) + 31) / 32) * sizeof(ULONG));
271     Session->SystemSpaceBitMap = ExAllocatePoolWithTag(NonPagedPool,
272                                                        BitmapSize,
273                                                        TAG_MM);
274     ASSERT(Session->SystemSpaceBitMap);
275     RtlInitializeBitMap(Session->SystemSpaceBitMap,
276                         (PULONG)(Session->SystemSpaceBitMap + 1),
277                         (ULONG)(Size / MI_SYSTEM_VIEW_BUCKET_SIZE));
278 
279     /* Set system space fully empty to begin with */
280     RtlClearAllBits(Session->SystemSpaceBitMap);
281 
282     /* Set default hash flags */
283     Session->SystemSpaceHashSize = 31;
284     Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
285     Session->SystemSpaceHashEntries = 0;
286 
287     /* Calculate how much space for the hash views we'll need */
288     AllocSize = sizeof(MMVIEW) * Session->SystemSpaceHashSize;
289     ASSERT(AllocSize < PAGE_SIZE);
290 
291     /* Allocate and zero the view table */
292     Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session == &MmSession ?
293                                                           NonPagedPool :
294                                                           PagedPool,
295                                                           AllocSize,
296                                                           TAG_MM);
297     ASSERT(Session->SystemSpaceViewTable != NULL);
298     RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize);
299 
300     /* Success */
301     return TRUE;
302 }
303 
304 PVOID
305 NTAPI
306 MiInsertInSystemSpace(IN PMMSESSION Session,
307                       IN ULONG Buckets,
308                       IN PCONTROL_AREA ControlArea)
309 {
310     PVOID Base;
311     ULONG Entry, Hash, i, HashSize;
312     PMMVIEW OldTable;
313     PAGED_CODE();
314 
315     /* Stay within 4GB */
316     ASSERT(Buckets < MI_SYSTEM_VIEW_BUCKET_SIZE);
317 
318     /* Lock system space */
319     KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
320 
321     /* Check if we're going to exhaust hash entries */
322     if ((Session->SystemSpaceHashEntries + 8) > Session->SystemSpaceHashSize)
323     {
324         /* Double the hash size */
325         HashSize = Session->SystemSpaceHashSize * 2;
326 
327         /* Save the old table and allocate a new one */
328         OldTable = Session->SystemSpaceViewTable;
329         Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session ==
330                                                               &MmSession ?
331                                                               NonPagedPool :
332                                                               PagedPool,
333                                                               HashSize *
334                                                               sizeof(MMVIEW),
335                                                               TAG_MM);
336         if (!Session->SystemSpaceViewTable)
337         {
338             /* Failed to allocate a new table, keep the old one for now */
339             Session->SystemSpaceViewTable = OldTable;
340         }
341         else
342         {
343             /* Clear the new table and set the new ahsh and key */
344             RtlZeroMemory(Session->SystemSpaceViewTable, HashSize * sizeof(MMVIEW));
345             Session->SystemSpaceHashSize = HashSize;
346             Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
347 
348             /* Loop the old table */
349             for (i = 0; i < Session->SystemSpaceHashSize / 2; i++)
350             {
351                 /* Check if the entry was valid */
352                 if (OldTable[i].Entry)
353                 {
354                     /* Re-hash the old entry and search for space in the new table */
355                     Hash = (OldTable[i].Entry >> 16) % Session->SystemSpaceHashKey;
356                     while (Session->SystemSpaceViewTable[Hash].Entry)
357                     {
358                         /* Loop back at the beginning if we had an overflow */
359                         if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
360                     }
361 
362                     /* Write the old entry in the new table */
363                     Session->SystemSpaceViewTable[Hash] = OldTable[i];
364                 }
365             }
366 
367             /* Free the old table */
368             ExFreePool(OldTable);
369         }
370     }
371 
372     /* Check if we ran out */
373     if (Session->SystemSpaceHashEntries == Session->SystemSpaceHashSize)
374     {
375         DPRINT1("Ran out of system view hash entries\n");
376         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
377         return NULL;
378     }
379 
380     /* Find space where to map this view */
381     i = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, Buckets, 0);
382     if (i == 0xFFFFFFFF)
383     {
384         /* Out of space, fail */
385         Session->BitmapFailures++;
386         DPRINT1("Out of system view space\n");
387         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
388         return NULL;
389     }
390 
391     /* Compute the base address */
392     Base = (PVOID)((ULONG_PTR)Session->SystemSpaceViewStart + (i * MI_SYSTEM_VIEW_BUCKET_SIZE));
393 
394     /* Get the hash entry for this allocation */
395     Entry = ((ULONG_PTR)Base & ~(MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) + Buckets;
396     Hash = (Entry >> 16) % Session->SystemSpaceHashKey;
397 
398     /* Loop hash entries until a free one is found */
399     while (Session->SystemSpaceViewTable[Hash].Entry)
400     {
401         /* Unless we overflow, in which case loop back at hash o */
402         if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
403     }
404 
405     /* Add this entry into the hash table */
406     Session->SystemSpaceViewTable[Hash].Entry = Entry;
407     Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;
408 
409     /* Hash entry found, increment total and return the base address */
410     Session->SystemSpaceHashEntries++;
411     KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
412     return Base;
413 }
414 
415 NTSTATUS
416 NTAPI
417 MiAddMappedPtes(IN PMMPTE FirstPte,
418                 IN PFN_NUMBER PteCount,
419                 IN PCONTROL_AREA ControlArea)
420 {
421     MMPTE TempPte;
422     PMMPTE PointerPte, ProtoPte, LastProtoPte, LastPte;
423     PSUBSECTION Subsection;
424 
425     /* ARM3 doesn't support this yet */
426     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
427     ASSERT(ControlArea->u.Flags.Rom == 0);
428     ASSERT(ControlArea->FilePointer == NULL);
429 
430     /* Sanity checks */
431     ASSERT(PteCount != 0);
432     ASSERT(ControlArea->NumberOfMappedViews >= 1);
433     ASSERT(ControlArea->NumberOfUserReferences >= 1);
434     ASSERT(ControlArea->NumberOfSectionReferences != 0);
435     ASSERT(ControlArea->u.Flags.BeingCreated == 0);
436     ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
437     ASSERT(ControlArea->u.Flags.BeingPurged == 0);
438 
439     /* Get the PTEs for the actual mapping */
440     PointerPte = FirstPte;
441     LastPte = FirstPte + PteCount;
442 
443     /* Get the prototype PTEs that desribe the section mapping in the subsection */
444     Subsection = (PSUBSECTION)(ControlArea + 1);
445     ProtoPte = Subsection->SubsectionBase;
446     LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
447 
448     /* Loop the PTEs for the mapping */
449     while (PointerPte < LastPte)
450     {
451         /* We may have run out of prototype PTEs in this subsection */
452         if (ProtoPte >= LastProtoPte)
453         {
454             /* But we don't handle this yet */
455             ASSERT(FALSE);
456         }
457 
458         /* The PTE should be completely clear */
459         ASSERT(PointerPte->u.Long == 0);
460 
461         /* Build the prototype PTE and write it */
462         MI_MAKE_PROTOTYPE_PTE(&TempPte, ProtoPte);
463         MI_WRITE_INVALID_PTE(PointerPte, TempPte);
464 
465         /* Keep going */
466         PointerPte++;
467         ProtoPte++;
468     }
469 
470     /* No failure path */
471     return STATUS_SUCCESS;
472 }
473 
474 VOID
475 NTAPI
476 MiFillSystemPageDirectory(IN PVOID Base,
477                           IN SIZE_T NumberOfBytes)
478 {
479     PMMPDE PointerPde, LastPde, SystemMapPde;
480     MMPDE TempPde;
481     PFN_NUMBER PageFrameIndex, ParentPage;
482     KIRQL OldIrql;
483     PAGED_CODE();
484 
485     /* Find the PDEs needed for this mapping */
486     PointerPde = MiAddressToPde(Base);
487     LastPde = MiAddressToPde((PVOID)((ULONG_PTR)Base + NumberOfBytes - 1));
488 
489 #if (_MI_PAGING_LEVELS == 2)
490     /* Find the system double-mapped PDE that describes this mapping */
491     SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
492 #else
493     /* We don't have a double mapping */
494     SystemMapPde = PointerPde;
495 #endif
496 
497     /* Use the PDE template and loop the PDEs */
498     TempPde = ValidKernelPde;
499     while (PointerPde <= LastPde)
500     {
501         /* Lock the PFN database */
502         OldIrql = MiAcquirePfnLock();
503 
504         /* Check if we don't already have this PDE mapped */
505         if (SystemMapPde->u.Hard.Valid == 0)
506         {
507             /* Grab a page for it */
508             MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
509             MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
510             PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR());
511             ASSERT(PageFrameIndex);
512             TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
513 
514 #if (_MI_PAGING_LEVELS == 2)
515             ParentPage = MmSystemPageDirectory[(PointerPde - MiAddressToPde(NULL)) / PDE_PER_PAGE];
516 #else
517             ParentPage = MiPdeToPpe(PointerPde)->u.Hard.PageFrameNumber;
518 #endif
519             /* Initialize its PFN entry, with the parent system page directory page table */
520             MiInitializePfnForOtherProcess(PageFrameIndex,
521                                            (PMMPTE)PointerPde,
522                                            ParentPage);
523 
524             /* Make the system PDE entry valid */
525             MI_WRITE_VALID_PDE(SystemMapPde, TempPde);
526 
527             /* The system PDE entry might be the PDE itself, so check for this */
528             if (PointerPde->u.Hard.Valid == 0)
529             {
530                 /* It's different, so make the real PDE valid too */
531                 MI_WRITE_VALID_PDE(PointerPde, TempPde);
532             }
533         }
534 
535         /* Release the lock and keep going with the next PDE */
536         MiReleasePfnLock(OldIrql);
537         SystemMapPde++;
538         PointerPde++;
539     }
540 }
541 
542 NTSTATUS
543 NTAPI
544 MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea,
545                           IN BOOLEAN FailIfSystemViews)
546 {
547     KIRQL OldIrql;
548 
549     /* Flag not yet supported */
550     ASSERT(FailIfSystemViews == FALSE);
551 
552     /* Lock the PFN database */
553     OldIrql = MiAcquirePfnLock();
554 
555     /* State not yet supported */
556     ASSERT(ControlArea->u.Flags.BeingPurged == 0);
557 
558     /* Increase the reference counts */
559     ControlArea->NumberOfMappedViews++;
560     ControlArea->NumberOfUserReferences++;
561     ASSERT(ControlArea->NumberOfSectionReferences != 0);
562 
563     /* Release the PFN lock and return success */
564     MiReleasePfnLock(OldIrql);
565     return STATUS_SUCCESS;
566 }
567 
568 PSUBSECTION
569 NTAPI
570 MiLocateSubsection(IN PMMVAD Vad,
571                    IN ULONG_PTR Vpn)
572 {
573     PSUBSECTION Subsection;
574     PCONTROL_AREA ControlArea;
575     ULONG_PTR PteOffset;
576 
577     /* Get the control area */
578     ControlArea = Vad->ControlArea;
579     ASSERT(ControlArea->u.Flags.Rom == 0);
580     ASSERT(ControlArea->u.Flags.Image == 0);
581     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
582 
583     /* Get the subsection */
584     Subsection = (PSUBSECTION)(ControlArea + 1);
585 
586     /* We only support single-subsection segments */
587     ASSERT(Subsection->SubsectionBase != NULL);
588     ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase);
589     ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]);
590 
591     /* Compute the PTE offset */
592     PteOffset = Vpn - Vad->StartingVpn;
593     PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase;
594 
595     /* Again, we only support single-subsection segments */
596     ASSERT(PteOffset < 0xF0000000);
597     ASSERT(PteOffset < Subsection->PtesInSubsection);
598 
599     /* Return the subsection */
600     return Subsection;
601 }
602 
603 VOID
604 NTAPI
605 MiSegmentDelete(IN PSEGMENT Segment)
606 {
607     PCONTROL_AREA ControlArea;
608     SEGMENT_FLAGS SegmentFlags;
609     PSUBSECTION Subsection;
610     PMMPTE PointerPte, LastPte, PteForProto;
611     PMMPFN Pfn1;
612     PFN_NUMBER PageFrameIndex;
613     MMPTE TempPte;
614     KIRQL OldIrql;
615 
616     /* Capture data */
617     SegmentFlags = Segment->SegmentFlags;
618     ControlArea = Segment->ControlArea;
619 
620     /* Make sure control area is on the right delete path */
621     ASSERT(ControlArea->u.Flags.BeingDeleted == 1);
622     ASSERT(ControlArea->WritableUserReferences == 0);
623 
624     /* These things are not supported yet */
625     ASSERT(ControlArea->DereferenceList.Flink == NULL);
626     ASSERT(!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File));
627     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
628     ASSERT(ControlArea->u.Flags.Rom == 0);
629 
630     /* Get the subsection and PTEs for this segment */
631     Subsection = (PSUBSECTION)(ControlArea + 1);
632     PointerPte = Subsection->SubsectionBase;
633     LastPte = PointerPte + Segment->NonExtendedPtes;
634 
635     /* Lock the PFN database */
636     OldIrql = MiAcquirePfnLock();
637 
638     /* Check if the master PTE is invalid */
639     PteForProto = MiAddressToPte(PointerPte);
640     if (!PteForProto->u.Hard.Valid)
641     {
642         /* Fault it in */
643         MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
644     }
645 
646     /* Loop all the segment PTEs */
647     while (PointerPte < LastPte)
648     {
649         /* Check if it's time to switch master PTEs if we passed a PDE boundary */
650         if (MiIsPteOnPdeBoundary(PointerPte) &&
651             (PointerPte != Subsection->SubsectionBase))
652         {
653             /* Check if the master PTE is invalid */
654             PteForProto = MiAddressToPte(PointerPte);
655             if (!PteForProto->u.Hard.Valid)
656             {
657                 /* Fault it in */
658                 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
659             }
660         }
661 
662         /* This should be a prototype PTE */
663         TempPte = *PointerPte;
664         ASSERT(SegmentFlags.LargePages == 0);
665         ASSERT(TempPte.u.Hard.Valid == 0);
666 
667         /* See if we should clean things up */
668         if (!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File))
669         {
670             /*
671              * This is a section backed by the pagefile. Now that it doesn't exist anymore,
672              * we can give everything back to the system.
673              */
674             ASSERT(TempPte.u.Soft.Prototype == 0);
675 
676             if (TempPte.u.Soft.Transition == 1)
677             {
678                 /* We can give the page back for other use */
679                 DPRINT("Releasing page for transition PTE %p\n", PointerPte);
680                 PageFrameIndex = PFN_FROM_PTE(&TempPte);
681                 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
682 
683                 /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */
684                 ASSERT(Pfn1->u3.ReferenceCount == 0);
685 
686                 /* And it should be in standby or modified list */
687                 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList));
688 
689                 /* Unlink it and put it back in free list */
690                 MiUnlinkPageFromList(Pfn1);
691 
692                 /* Temporarily mark this as active and make it free again */
693                 Pfn1->u3.e1.PageLocation = ActiveAndValid;
694                 MI_SET_PFN_DELETED(Pfn1);
695 
696                 MiInsertPageInFreeList(PageFrameIndex);
697             }
698             else if (TempPte.u.Soft.PageFileHigh != 0)
699             {
700                 /* Should not happen for now */
701                 ASSERT(FALSE);
702             }
703         }
704         else
705         {
706             /* unsupported for now */
707             ASSERT(FALSE);
708 
709             /* File-backed section must have prototype PTEs */
710             ASSERT(TempPte.u.Soft.Prototype == 1);
711         }
712 
713         /* Zero the PTE and keep going */
714         PointerPte->u.Long = 0;
715         PointerPte++;
716     }
717 
718     /* Release the PFN lock */
719     MiReleasePfnLock(OldIrql);
720 
721     /* Free the structures */
722     ExFreePool(ControlArea);
723     ExFreePool(Segment);
724 }
725 
726 VOID
727 NTAPI
728 MiCheckControlArea(IN PCONTROL_AREA ControlArea,
729                    IN KIRQL OldIrql)
730 {
731     BOOLEAN DeleteSegment = FALSE;
732     MI_ASSERT_PFN_LOCK_HELD();
733 
734     /* Check if this is the last reference or view */
735     if (!(ControlArea->NumberOfMappedViews) &&
736         !(ControlArea->NumberOfSectionReferences))
737     {
738         /* There should be no more user references either */
739         ASSERT(ControlArea->NumberOfUserReferences == 0);
740 
741         /* Not yet supported */
742         ASSERT(ControlArea->FilePointer == NULL);
743 
744         /* The control area is being destroyed */
745         ControlArea->u.Flags.BeingDeleted = TRUE;
746         DeleteSegment = TRUE;
747     }
748 
749     /* Release the PFN lock */
750     MiReleasePfnLock(OldIrql);
751 
752     /* Delete the segment if needed */
753     if (DeleteSegment)
754     {
755         /* No more user write references at all */
756         ASSERT(ControlArea->WritableUserReferences == 0);
757         MiSegmentDelete(ControlArea->Segment);
758     }
759 }
760 
761 VOID
762 NTAPI
763 MiDereferenceControlArea(IN PCONTROL_AREA ControlArea)
764 {
765     KIRQL OldIrql;
766 
767     /* Lock the PFN database */
768     OldIrql = MiAcquirePfnLock();
769 
770     /* Drop reference counts */
771     ControlArea->NumberOfMappedViews--;
772     ControlArea->NumberOfUserReferences--;
773 
774     /* Check if it's time to delete the CA. This releases the lock */
775     MiCheckControlArea(ControlArea, OldIrql);
776 }
777 
778 VOID
779 NTAPI
780 MiRemoveMappedView(IN PEPROCESS CurrentProcess,
781                    IN PMMVAD Vad)
782 {
783     KIRQL OldIrql;
784     PCONTROL_AREA ControlArea;
785     PETHREAD CurrentThread = PsGetCurrentThread();
786 
787     /* Get the control area */
788     ControlArea = Vad->ControlArea;
789 
790     /* We only support non-extendable, non-image, pagefile-backed regular sections */
791     ASSERT(Vad->u.VadFlags.VadType == VadNone);
792     ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE);
793     ASSERT(ControlArea);
794     ASSERT(ControlArea->FilePointer == NULL);
795 
796     /* Delete the actual virtual memory pages */
797     MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
798                              (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
799                              Vad);
800 
801     /* Release the working set */
802     MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
803 
804     /* Lock the PFN database */
805     OldIrql = MiAcquirePfnLock();
806 
807     /* Remove references */
808     ControlArea->NumberOfMappedViews--;
809     ControlArea->NumberOfUserReferences--;
810 
811     /* Check if it should be destroyed */
812     MiCheckControlArea(ControlArea, OldIrql);
813 }
814 
815 NTSTATUS
816 NTAPI
817 MiUnmapViewOfSection(IN PEPROCESS Process,
818                      IN PVOID BaseAddress,
819                      IN ULONG Flags)
820 {
821     PMEMORY_AREA MemoryArea;
822     BOOLEAN Attached = FALSE;
823     KAPC_STATE ApcState;
824     PMMVAD Vad;
825     PVOID DbgBase = NULL;
826     SIZE_T RegionSize;
827     NTSTATUS Status;
828     PETHREAD CurrentThread = PsGetCurrentThread();
829     PEPROCESS CurrentProcess = PsGetCurrentProcess();
830     PAGED_CODE();
831 
832     /* Check for Mm Region */
833     MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, BaseAddress);
834     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
835     {
836         /* Call Mm API */
837         return MiRosUnmapViewOfSection(Process, BaseAddress, Process->ProcessExiting);
838     }
839 
840     /* Check if we should attach to the process */
841     if (CurrentProcess != Process)
842     {
843         /* The process is different, do an attach */
844         KeStackAttachProcess(&Process->Pcb, &ApcState);
845         Attached = TRUE;
846     }
847 
848     /* Check if we need to lock the address space */
849     if (!Flags) MmLockAddressSpace(&Process->Vm);
850 
851     /* Check if the process is already daed */
852     if (Process->VmDeleted)
853     {
854         /* Fail the call */
855         DPRINT1("Process died!\n");
856         if (!Flags) MmUnlockAddressSpace(&Process->Vm);
857         Status = STATUS_PROCESS_IS_TERMINATING;
858         goto Quickie;
859     }
860 
861     /* Find the VAD for the address and make sure it's a section VAD */
862     Vad = MiLocateAddress(BaseAddress);
863     if (!(Vad) || (Vad->u.VadFlags.PrivateMemory))
864     {
865         /* Couldn't find it, or invalid VAD, fail */
866         DPRINT1("No VAD or invalid VAD\n");
867         if (!Flags) MmUnlockAddressSpace(&Process->Vm);
868         Status = STATUS_NOT_MAPPED_VIEW;
869         goto Quickie;
870     }
871 
872     /* We should be attached */
873     ASSERT(Process == PsGetCurrentProcess());
874 
875     /* We need the base address for the debugger message on image-backed VADs */
876     if (Vad->u.VadFlags.VadType == VadImageMap)
877     {
878         DbgBase = (PVOID)(Vad->StartingVpn >> PAGE_SHIFT);
879     }
880 
881     /* Compute the size of the VAD region */
882     RegionSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
883 
884     /* For SEC_NO_CHANGE sections, we need some extra checks */
885     if (Vad->u.VadFlags.NoChange == 1)
886     {
887         /* Are we allowed to mess with this VAD? */
888         Status = MiCheckSecuredVad(Vad,
889                                    (PVOID)(Vad->StartingVpn >> PAGE_SHIFT),
890                                    RegionSize,
891                                    MM_DELETE_CHECK);
892         if (!NT_SUCCESS(Status))
893         {
894             /* We failed */
895             DPRINT1("Trying to unmap protected VAD!\n");
896             if (!Flags) MmUnlockAddressSpace(&Process->Vm);
897             goto Quickie;
898         }
899     }
900 
901     /* Not currently supported */
902     ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
903 
904     /* FIXME: Remove VAD charges */
905 
906     /* Lock the working set */
907     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
908 
909     /* Remove the VAD */
910     ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
911     MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
912 
913     /* Remove the PTEs for this view, which also releases the working set lock */
914     MiRemoveMappedView(Process, Vad);
915 
916     /* FIXME: Remove commitment */
917 
918     /* Update performance counter and release the lock */
919     Process->VirtualSize -= RegionSize;
920     if (!Flags) MmUnlockAddressSpace(&Process->Vm);
921 
922     /* Destroy the VAD and return success */
923     ExFreePool(Vad);
924     Status = STATUS_SUCCESS;
925 
926     /* Failure and success case -- send debugger message, detach, and return */
927 Quickie:
928     if (DbgBase) DbgkUnMapViewOfSection(DbgBase);
929     if (Attached) KeUnstackDetachProcess(&ApcState);
930     return Status;
931 }
932 
933 NTSTATUS
934 NTAPI
935 MiSessionCommitPageTables(IN PVOID StartVa,
936                           IN PVOID EndVa)
937 {
938     KIRQL OldIrql;
939     ULONG Color, Index;
940     PMMPDE StartPde, EndPde;
941     MMPDE TempPde = ValidKernelPdeLocal;
942     PMMPFN Pfn1;
943     PFN_NUMBER PageCount = 0, ActualPages = 0, PageFrameNumber;
944 
945     /* Windows sanity checks */
946     ASSERT(StartVa >= (PVOID)MmSessionBase);
947     ASSERT(EndVa < (PVOID)MiSessionSpaceEnd);
948     ASSERT(PAGE_ALIGN(EndVa) == EndVa);
949 
950     /* Get the start and end PDE, then loop each one */
951     StartPde = MiAddressToPde(StartVa);
952     EndPde = MiAddressToPde((PVOID)((ULONG_PTR)EndVa - 1));
953     Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22;
954     while (StartPde <= EndPde)
955     {
956 #ifndef _M_AMD64
957         /* If we don't already have a page table for it, increment count */
958         if (MmSessionSpace->PageTables[Index].u.Long == 0) PageCount++;
959 #endif
960         /* Move to the next one */
961         StartPde++;
962         Index++;
963     }
964 
965     /* If there's no page tables to create, bail out */
966     if (PageCount == 0) return STATUS_SUCCESS;
967 
968     /* Reset the start PDE and index */
969     StartPde = MiAddressToPde(StartVa);
970     Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22;
971 
972     /* Loop each PDE while holding the working set lock */
973 //  MiLockWorkingSet(PsGetCurrentThread(),
974 //                   &MmSessionSpace->GlobalVirtualAddress->Vm);
975 #ifdef _M_AMD64
976 _WARN("MiSessionCommitPageTables halfplemented for amd64")
977     DBG_UNREFERENCED_LOCAL_VARIABLE(OldIrql);
978     DBG_UNREFERENCED_LOCAL_VARIABLE(Color);
979     DBG_UNREFERENCED_LOCAL_VARIABLE(TempPde);
980     DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn1);
981     DBG_UNREFERENCED_LOCAL_VARIABLE(PageFrameNumber);
982     ASSERT(FALSE);
983 #else
984     while (StartPde <= EndPde)
985     {
986         /* Check if we already have a page table */
987         if (MmSessionSpace->PageTables[Index].u.Long == 0)
988         {
989             /* We don't, so the PDE shouldn't be ready yet */
990             ASSERT(StartPde->u.Hard.Valid == 0);
991 
992             /* ReactOS check to avoid MiEnsureAvailablePageOrWait */
993             ASSERT(MmAvailablePages >= 32);
994 
995             /* Acquire the PFN lock and grab a zero page */
996             OldIrql = MiAcquirePfnLock();
997             MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
998             MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
999             Color = (++MmSessionSpace->Color) & MmSecondaryColorMask;
1000             PageFrameNumber = MiRemoveZeroPage(Color);
1001             TempPde.u.Hard.PageFrameNumber = PageFrameNumber;
1002             MI_WRITE_VALID_PDE(StartPde, TempPde);
1003 
1004             /* Write the page table in session space structure */
1005             ASSERT(MmSessionSpace->PageTables[Index].u.Long == 0);
1006             MmSessionSpace->PageTables[Index] = TempPde;
1007 
1008             /* Initialize the PFN */
1009             MiInitializePfnForOtherProcess(PageFrameNumber,
1010                                            StartPde,
1011                                            MmSessionSpace->SessionPageDirectoryIndex);
1012 
1013             /* And now release the lock */
1014             MiReleasePfnLock(OldIrql);
1015 
1016             /* Get the PFN entry and make sure there's no event for it */
1017             Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
1018             ASSERT(Pfn1->u1.Event == NULL);
1019 
1020             /* Increment the number of pages */
1021             ActualPages++;
1022         }
1023 
1024         /* Move to the next PDE */
1025         StartPde++;
1026         Index++;
1027     }
1028 #endif
1029 
1030     /* Make sure we didn't do more pages than expected */
1031     ASSERT(ActualPages <= PageCount);
1032 
1033     /* Release the working set lock */
1034 //  MiUnlockWorkingSet(PsGetCurrentThread(),
1035 //                     &MmSessionSpace->GlobalVirtualAddress->Vm);
1036 
1037 
1038     /* If we did at least one page... */
1039     if (ActualPages)
1040     {
1041         /* Update the performance counters! */
1042         InterlockedExchangeAddSizeT(&MmSessionSpace->NonPageablePages, ActualPages);
1043         InterlockedExchangeAddSizeT(&MmSessionSpace->CommittedPages, ActualPages);
1044     }
1045 
1046     /* Return status */
1047     return STATUS_SUCCESS;
1048 }
1049 
1050 NTSTATUS
1051 NTAPI
1052 MiMapViewInSystemSpace(IN PVOID Section,
1053                        IN PMMSESSION Session,
1054                        OUT PVOID *MappedBase,
1055                        IN OUT PSIZE_T ViewSize)
1056 {
1057     PVOID Base;
1058     PCONTROL_AREA ControlArea;
1059     ULONG Buckets, SectionSize;
1060     NTSTATUS Status;
1061     PAGED_CODE();
1062 
1063     /* Get the control area, check for any flags ARM3 doesn't yet support */
1064     ControlArea = ((PSECTION)Section)->Segment->ControlArea;
1065     ASSERT(ControlArea->u.Flags.Image == 0);
1066     ASSERT(ControlArea->FilePointer == NULL);
1067     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1068     ASSERT(ControlArea->u.Flags.Rom == 0);
1069     ASSERT(ControlArea->u.Flags.WasPurged == 0);
1070 
1071     /* Increase the reference and map count on the control area, no purges yet */
1072     Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1073     ASSERT(NT_SUCCESS(Status));
1074 
1075     /* Get the section size at creation time */
1076     SectionSize = ((PSECTION)Section)->SizeOfSection.LowPart;
1077 
1078     /* If the caller didn't specify a view size, assume the whole section */
1079     if (!(*ViewSize)) *ViewSize = SectionSize;
1080 
1081     /* Check if the caller wanted a larger section than the view */
1082     if (*ViewSize > SectionSize)
1083     {
1084         /* Fail */
1085         DPRINT1("View is too large\n");
1086         MiDereferenceControlArea(ControlArea);
1087         return STATUS_INVALID_VIEW_SIZE;
1088     }
1089 
1090     /* Get the number of 64K buckets required for this mapping */
1091     Buckets = (ULONG)(*ViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE);
1092     if (*ViewSize & (MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) Buckets++;
1093 
1094     /* Check if the view is more than 4GB large */
1095     if (Buckets >= MI_SYSTEM_VIEW_BUCKET_SIZE)
1096     {
1097         /* Fail */
1098         DPRINT1("View is too large\n");
1099         MiDereferenceControlArea(ControlArea);
1100         return STATUS_INVALID_VIEW_SIZE;
1101     }
1102 
1103     /* Insert this view into system space and get a base address for it */
1104     Base = MiInsertInSystemSpace(Session, Buckets, ControlArea);
1105     if (!Base)
1106     {
1107         /* Fail */
1108         DPRINT1("Out of system space\n");
1109         MiDereferenceControlArea(ControlArea);
1110         return STATUS_NO_MEMORY;
1111     }
1112 
1113     /* What's the underlying session? */
1114     if (Session == &MmSession)
1115     {
1116         /* Create the PDEs needed for this mapping, and double-map them if needed */
1117         MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE);
1118         Status = STATUS_SUCCESS;
1119     }
1120     else
1121     {
1122         /* Create the PDEs needed for this mapping */
1123         Status = MiSessionCommitPageTables(Base,
1124                                            (PVOID)((ULONG_PTR)Base +
1125                                            Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE));
1126         ASSERT(NT_SUCCESS(Status));
1127     }
1128 
1129     /* Create the actual prototype PTEs for this mapping */
1130     Status = MiAddMappedPtes(MiAddressToPte(Base),
1131                              BYTES_TO_PAGES(*ViewSize),
1132                              ControlArea);
1133     ASSERT(NT_SUCCESS(Status));
1134 
1135     /* Return the base adress of the mapping and success */
1136     *MappedBase = Base;
1137     return STATUS_SUCCESS;
1138 }
1139 
1140 VOID
1141 NTAPI
1142 MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea)
1143 {
1144     KIRQL OldIrql;
1145 
1146     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1147 
1148     OldIrql = MiAcquirePfnLock();
1149     ControlArea->u.Flags.DebugSymbolsLoaded |= 1;
1150 
1151     ASSERT(OldIrql <= APC_LEVEL);
1152     MiReleasePfnLock(OldIrql);
1153     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1154 }
1155 
1156 VOID
1157 NTAPI
1158 MiLoadUserSymbols(IN PCONTROL_AREA ControlArea,
1159                   IN PVOID BaseAddress,
1160                   IN PEPROCESS Process)
1161 {
1162     NTSTATUS Status;
1163     ANSI_STRING FileNameA;
1164     PLIST_ENTRY NextEntry;
1165     PUNICODE_STRING FileName;
1166     PIMAGE_NT_HEADERS NtHeaders;
1167     PLDR_DATA_TABLE_ENTRY LdrEntry;
1168 
1169     FileName = &ControlArea->FilePointer->FileName;
1170     if (FileName->Length == 0)
1171     {
1172         return;
1173     }
1174 
1175     /* Acquire module list lock */
1176     KeEnterCriticalRegion();
1177     ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
1178 
1179     /* Browse list to try to find current module */
1180     for (NextEntry = MmLoadedUserImageList.Flink;
1181          NextEntry != &MmLoadedUserImageList;
1182          NextEntry = NextEntry->Flink)
1183     {
1184         /* Get the entry */
1185         LdrEntry = CONTAINING_RECORD(NextEntry,
1186                                      LDR_DATA_TABLE_ENTRY,
1187                                      InLoadOrderLinks);
1188 
1189         /* If already in the list, increase load count */
1190         if (LdrEntry->DllBase == BaseAddress)
1191         {
1192             ++LdrEntry->LoadCount;
1193             break;
1194         }
1195     }
1196 
1197     /* Not in the list, we'll add it */
1198     if (NextEntry == &MmLoadedUserImageList)
1199     {
1200         /* Allocate our element, taking to the name string and its null char */
1201         LdrEntry = ExAllocatePoolWithTag(NonPagedPool, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry), 'bDmM');
1202         if (LdrEntry)
1203         {
1204             memset(LdrEntry, 0, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry));
1205 
1206             _SEH2_TRY
1207             {
1208                 /* Get image checksum and size */
1209                 NtHeaders = RtlImageNtHeader(BaseAddress);
1210                 if (NtHeaders)
1211                 {
1212                     LdrEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
1213                     LdrEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
1214                 }
1215             }
1216             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1217             {
1218                 ExFreePoolWithTag(LdrEntry, 'bDmM');
1219                 _SEH2_YIELD(return);
1220             }
1221             _SEH2_END;
1222 
1223             /* Fill all the details */
1224             LdrEntry->DllBase = BaseAddress;
1225             LdrEntry->FullDllName.Buffer = (PVOID)((ULONG_PTR)LdrEntry + sizeof(*LdrEntry));
1226             LdrEntry->FullDllName.Length = FileName->Length;
1227             LdrEntry->FullDllName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL);
1228             memcpy(LdrEntry->FullDllName.Buffer, FileName->Buffer, FileName->Length);
1229             LdrEntry->FullDllName.Buffer[LdrEntry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1230             LdrEntry->LoadCount = 1;
1231 
1232             /* Insert! */
1233             InsertHeadList(&MmLoadedUserImageList, &LdrEntry->InLoadOrderLinks);
1234         }
1235     }
1236 
1237     /* Release locks */
1238     ExReleaseResourceLite(&PsLoadedModuleResource);
1239     KeLeaveCriticalRegion();
1240 
1241     /* Load symbols */
1242     Status = RtlUnicodeStringToAnsiString(&FileNameA, FileName, TRUE);
1243     if (NT_SUCCESS(Status))
1244     {
1245         DbgLoadImageSymbols(&FileNameA, BaseAddress, (ULONG_PTR)Process->UniqueProcessId);
1246         RtlFreeAnsiString(&FileNameA);
1247     }
1248 }
1249 
1250 NTSTATUS
1251 NTAPI
1252 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
1253                        IN PEPROCESS Process,
1254                        IN PVOID *BaseAddress,
1255                        IN PLARGE_INTEGER SectionOffset,
1256                        IN PSIZE_T ViewSize,
1257                        IN PSECTION Section,
1258                        IN SECTION_INHERIT InheritDisposition,
1259                        IN ULONG ProtectionMask,
1260                        IN SIZE_T CommitSize,
1261                        IN ULONG_PTR ZeroBits,
1262                        IN ULONG AllocationType)
1263 {
1264     PMMVAD_LONG Vad;
1265     ULONG_PTR StartAddress;
1266     ULONG_PTR ViewSizeInPages;
1267     PSUBSECTION Subsection;
1268     PSEGMENT Segment;
1269     PFN_NUMBER PteOffset;
1270     NTSTATUS Status;
1271     ULONG QuotaCharge = 0, QuotaExcess = 0;
1272     PMMPTE PointerPte, LastPte;
1273     MMPTE TempPte;
1274     ULONG Granularity = MM_VIRTMEM_GRANULARITY;
1275 
1276     DPRINT("Mapping ARM3 data section\n");
1277 
1278     /* Get the segment for this section */
1279     Segment = ControlArea->Segment;
1280 
1281 #ifdef _M_IX86
1282     /* ALlow being less restrictive on x86. */
1283     if (AllocationType & MEM_DOS_LIM)
1284         Granularity = PAGE_SIZE;
1285 #endif
1286 
1287     /* One can only reserve a file-based mapping, not shared memory! */
1288     if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer))
1289     {
1290         return STATUS_INVALID_PARAMETER_9;
1291     }
1292 
1293     /* First, increase the map count. No purging is supported yet */
1294     Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1295     if (!NT_SUCCESS(Status)) return Status;
1296 
1297     /* Check if the caller specified the view size */
1298     if (!(*ViewSize))
1299     {
1300         /* The caller did not, so pick a 64K aligned view size based on the offset */
1301         SectionOffset->LowPart &= ~(_64K - 1);
1302         *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
1303     }
1304     else
1305     {
1306         /* A size was specified, align it to a 64K boundary */
1307         *ViewSize += SectionOffset->LowPart & (_64K - 1);
1308 
1309         /* Align the offset as well to make this an aligned map */
1310         SectionOffset->LowPart &= ~((ULONG)_64K - 1);
1311     }
1312 
1313     /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1314     ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0);
1315 
1316     /* It's illegal to try to map more than overflows a LONG_PTR */
1317     if (*ViewSize >= MAXLONG_PTR)
1318     {
1319         MiDereferenceControlArea(ControlArea);
1320         return STATUS_INVALID_VIEW_SIZE;
1321     }
1322 
1323     /* Windows ASSERTs for this flag */
1324     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1325 
1326     /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1327     ASSERT(ControlArea->u.Flags.Rom == 0);
1328     Subsection = (PSUBSECTION)(ControlArea + 1);
1329 
1330     /* Sections with extended segments are not supported in ARM3 */
1331     ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0);
1332 
1333     /* Within this section, figure out which PTEs will describe the view */
1334     PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT);
1335 
1336     /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1337     ASSERT(PteOffset < Segment->TotalNumberOfPtes);
1338     ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset);
1339 
1340     /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1341     ASSERT(PteOffset < Subsection->PtesInSubsection);
1342 
1343     /* In ARM3, only page-file backed sections (shared memory) are supported now */
1344     ASSERT(ControlArea->FilePointer == NULL);
1345 
1346     /* Windows ASSERTs for this too -- there must be a subsection base address */
1347     ASSERT(Subsection->SubsectionBase != NULL);
1348 
1349     /* Compute how much commit space the segment will take */
1350     if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes))
1351     {
1352         /* Charge for the maximum pages */
1353         QuotaCharge = BYTES_TO_PAGES(CommitSize);
1354     }
1355 
1356     /* ARM3 does not currently support large pages */
1357     ASSERT(Segment->SegmentFlags.LargePages == 0);
1358 
1359     /* Calculate how many pages the region spans */
1360     ViewSizeInPages = BYTES_TO_PAGES(*ViewSize);
1361 
1362     /* A VAD can now be allocated. Do so and zero it out */
1363     /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1364     ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */
1365     Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
1366     if (!Vad)
1367     {
1368         MiDereferenceControlArea(ControlArea);
1369         return STATUS_INSUFFICIENT_RESOURCES;
1370     }
1371 
1372     RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
1373     Vad->u4.Banked = (PVOID)(ULONG_PTR)0xDEADBABEDEADBABEULL;
1374 
1375     /* Write all the data required in the VAD for handling a fault */
1376     Vad->ControlArea = ControlArea;
1377     Vad->u.VadFlags.CommitCharge = 0;
1378     Vad->u.VadFlags.Protection = ProtectionMask;
1379     Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16);
1380     Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
1381     if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange))
1382     {
1383         /* This isn't really implemented yet, but handle setting the flag */
1384         Vad->u.VadFlags.NoChange = 1;
1385         Vad->u2.VadFlags2.SecNoChange = 1;
1386     }
1387 
1388     /* Finally, write down the first and last prototype PTE */
1389     Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
1390     PteOffset += ViewSizeInPages - 1;
1391     ASSERT(PteOffset < Subsection->PtesInSubsection);
1392     Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
1393 
1394     /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1395     ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
1396 
1397     /* FIXME: Should setup VAD bitmap */
1398     Status = STATUS_SUCCESS;
1399 
1400     /* Check if anything was committed */
1401     if (QuotaCharge)
1402     {
1403         /* Set the start and end PTE addresses, and pick the template PTE */
1404         PointerPte = Vad->FirstPrototypePte;
1405         LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1406         TempPte = Segment->SegmentPteTemplate;
1407 
1408         /* Acquire the commit lock and loop all prototype PTEs to be committed */
1409         KeAcquireGuardedMutex(&MmSectionCommitMutex);
1410         while (PointerPte < LastPte)
1411         {
1412             /* Make sure the PTE is already invalid */
1413             if (PointerPte->u.Long == 0)
1414             {
1415                 /* And write the invalid PTE */
1416                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1417             }
1418             else
1419             {
1420                 /* The PTE is valid, so skip it */
1421                 QuotaExcess++;
1422             }
1423 
1424             /* Move to the next PTE */
1425             PointerPte++;
1426         }
1427 
1428         /* Now check how many pages exactly we committed, and update accounting */
1429         ASSERT(QuotaCharge >= QuotaExcess);
1430         QuotaCharge -= QuotaExcess;
1431         Segment->NumberOfCommittedPages += QuotaCharge;
1432         ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes);
1433 
1434         /* Now that we're done, release the lock */
1435         KeReleaseGuardedMutex(&MmSectionCommitMutex);
1436     }
1437 
1438     /* Is it SEC_BASED, or did the caller manually specify an address? */
1439     if (*BaseAddress != NULL)
1440     {
1441         /* Just align what the caller gave us */
1442         StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity);
1443     }
1444     else if (Section->Address.StartingVpn != 0)
1445     {
1446         /* It is a SEC_BASED mapping, use the address that was generated */
1447         StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart;
1448     }
1449     else
1450     {
1451         StartAddress = 0;
1452     }
1453 
1454     /* Insert the VAD */
1455     Status = MiInsertVadEx((PMMVAD)Vad,
1456                            &StartAddress,
1457                            ViewSizeInPages * PAGE_SIZE,
1458                            MAXULONG_PTR >> ZeroBits,
1459                            Granularity,
1460                            AllocationType);
1461     if (!NT_SUCCESS(Status))
1462     {
1463         return Status;
1464     }
1465 
1466     /* Windows stores this for accounting purposes, do so as well */
1467     if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress;
1468 
1469     /* Finally, let the caller know where, and for what size, the view was mapped */
1470     *ViewSize = ViewSizeInPages * PAGE_SIZE;
1471     *BaseAddress = (PVOID)StartAddress;
1472     DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize);
1473     return STATUS_SUCCESS;
1474 }
1475 
1476 VOID
1477 NTAPI
1478 MiSubsectionConsistent(IN PSUBSECTION Subsection)
1479 {
1480     /* ReactOS only supports systems with 4K pages and 4K sectors */
1481     ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0);
1482 
1483     /* Therefore, then number of PTEs should be equal to the number of sectors */
1484     if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection)
1485     {
1486         /* Break and warn if this is inconsistent */
1487         DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1488                 Subsection->NumberOfFullSectors, Subsection->PtesInSubsection);
1489         DbgBreakPoint();
1490     }
1491 }
1492 
1493 NTSTATUS
1494 NTAPI
1495 MiCreateDataFileMap(IN PFILE_OBJECT File,
1496                     OUT PSEGMENT *Segment,
1497                     IN PSIZE_T MaximumSize,
1498                     IN ULONG SectionPageProtection,
1499                     IN ULONG AllocationAttributes,
1500                     IN ULONG IgnoreFileSizing)
1501 {
1502     /* Not yet implemented */
1503     ASSERT(FALSE);
1504     *Segment = NULL;
1505     return STATUS_NOT_IMPLEMENTED;
1506 }
1507 
1508 NTSTATUS
1509 NTAPI
1510 MiCreatePagingFileMap(OUT PSEGMENT *Segment,
1511                       IN PSIZE_T MaximumSize,
1512                       IN ULONG ProtectionMask,
1513                       IN ULONG AllocationAttributes)
1514 {
1515     SIZE_T SizeLimit;
1516     PFN_COUNT PteCount;
1517     PMMPTE PointerPte;
1518     MMPTE TempPte;
1519     PCONTROL_AREA ControlArea;
1520     PSEGMENT NewSegment;
1521     PSUBSECTION Subsection;
1522     PAGED_CODE();
1523 
1524     /* No large pages in ARM3 yet */
1525     ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
1526 
1527     /* Pagefile-backed sections need a known size */
1528     if (!(*MaximumSize)) return STATUS_INVALID_PARAMETER_4;
1529 
1530     /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1531     SizeLimit = MAXULONG_PTR - sizeof(SEGMENT);
1532     SizeLimit /= sizeof(MMPTE);
1533     SizeLimit <<= PAGE_SHIFT;
1534 
1535     /* Fail if this size is too big */
1536     if (*MaximumSize > SizeLimit) return STATUS_SECTION_TOO_BIG;
1537 
1538     /* Calculate how many Prototype PTEs will be needed */
1539     PteCount = (PFN_COUNT)((*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT);
1540 
1541     /* For commited memory, we must have a valid protection mask */
1542     if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0);
1543 
1544     /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1545     NewSegment = ExAllocatePoolWithTag(PagedPool,
1546                                        sizeof(SEGMENT) +
1547                                        sizeof(MMPTE) * (PteCount - 1),
1548                                        'tSmM');
1549     ASSERT(NewSegment);
1550     *Segment = NewSegment;
1551 
1552     /* Now allocate the control area, which has the subsection structure */
1553     ControlArea = ExAllocatePoolWithTag(NonPagedPool,
1554                                         sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
1555                                         'tCmM');
1556     ASSERT(ControlArea);
1557 
1558     /* And zero it out, filling the basic segmnet pointer and reference fields */
1559     RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
1560     ControlArea->Segment = NewSegment;
1561     ControlArea->NumberOfSectionReferences = 1;
1562     ControlArea->NumberOfUserReferences = 1;
1563 
1564     /* Convert allocation attributes to control area flags */
1565     if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1;
1566     if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1;
1567     if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1;
1568 
1569     /* We just allocated it */
1570     ControlArea->u.Flags.BeingCreated = 1;
1571 
1572     /* The subsection follows, write the mask, PTE count and point back to the CA */
1573     Subsection = (PSUBSECTION)(ControlArea + 1);
1574     Subsection->ControlArea = ControlArea;
1575     Subsection->PtesInSubsection = PteCount;
1576     Subsection->u.SubsectionFlags.Protection = ProtectionMask;
1577 
1578     /* Zero out the segment's prototype PTEs, and link it with the control area */
1579     PointerPte = &NewSegment->ThePtes[0];
1580     RtlZeroMemory(NewSegment, sizeof(SEGMENT));
1581     NewSegment->PrototypePte = PointerPte;
1582     NewSegment->ControlArea = ControlArea;
1583 
1584     /* Save some extra accounting data for the segment as well */
1585     NewSegment->u1.CreatingProcess = PsGetCurrentProcess();
1586     NewSegment->SizeOfSegment = PteCount * PAGE_SIZE;
1587     NewSegment->TotalNumberOfPtes = PteCount;
1588     NewSegment->NonExtendedPtes = PteCount;
1589 
1590     /* The subsection's base address is the first Prototype PTE in the segment */
1591     Subsection->SubsectionBase = PointerPte;
1592 
1593     /* Start with an empty PTE, unless this is a commit operation */
1594     TempPte.u.Long = 0;
1595     if (AllocationAttributes & SEC_COMMIT)
1596     {
1597         /* In which case, write down the protection mask in the Prototype PTEs */
1598         TempPte.u.Soft.Protection = ProtectionMask;
1599 
1600         /* For accounting, also mark these pages as being committed */
1601         NewSegment->NumberOfCommittedPages = PteCount;
1602     }
1603 
1604     /* The template PTE itself for the segment should also have the mask set */
1605     NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
1606 
1607     /* Write out the prototype PTEs, for now they're simply demand zero */
1608 #ifdef _WIN64
1609     RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1610 #else
1611     RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1612 #endif
1613     return STATUS_SUCCESS;
1614 }
1615 
1616 NTSTATUS
1617 NTAPI
1618 MiGetFileObjectForSectionAddress(
1619     IN PVOID Address,
1620     OUT PFILE_OBJECT *FileObject)
1621 {
1622     PMMVAD Vad;
1623     PCONTROL_AREA ControlArea;
1624 
1625     /* Get the VAD */
1626     Vad = MiLocateAddress(Address);
1627     if (Vad == NULL)
1628     {
1629         /* Fail, the address does not exist */
1630         DPRINT1("Invalid address\n");
1631         return STATUS_INVALID_ADDRESS;
1632     }
1633 
1634     /* Check if this is a RosMm memory area */
1635     if (Vad->u.VadFlags.Spare != 0)
1636     {
1637         PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad;
1638         PROS_SECTION_OBJECT Section;
1639 
1640         /* Check if it's a section view (RosMm section) */
1641         if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1642         {
1643             /* Get the section pointer to the SECTION_OBJECT */
1644             Section = MemoryArea->Data.SectionData.Section;
1645             *FileObject = Section->FileObject;
1646         }
1647         else
1648         {
1649             ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
1650             DPRINT1("Address is a cache section!\n");
1651             return STATUS_SECTION_NOT_IMAGE;
1652         }
1653     }
1654     else
1655     {
1656         /* Make sure it's not a VM VAD */
1657         if (Vad->u.VadFlags.PrivateMemory == 1)
1658         {
1659             DPRINT1("Address is not a section\n");
1660             return STATUS_SECTION_NOT_IMAGE;
1661         }
1662 
1663         /* Get the control area */
1664         ControlArea = Vad->ControlArea;
1665         if (!(ControlArea) || !(ControlArea->u.Flags.Image))
1666         {
1667             DPRINT1("Address is not a section\n");
1668             return STATUS_SECTION_NOT_IMAGE;
1669         }
1670 
1671         /* Get the file object */
1672         *FileObject = ControlArea->FilePointer;
1673     }
1674 
1675     /* Return success */
1676     return STATUS_SUCCESS;
1677 }
1678 
1679 PFILE_OBJECT
1680 NTAPI
1681 MmGetFileObjectForSection(IN PVOID SectionObject)
1682 {
1683     PSECTION_OBJECT Section;
1684     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1685     ASSERT(SectionObject != NULL);
1686 
1687     /* Check if it's an ARM3, or ReactOS section */
1688     if (MiIsRosSectionObject(SectionObject) == FALSE)
1689     {
1690         /* Return the file pointer stored in the control area */
1691         Section = SectionObject;
1692         return Section->Segment->ControlArea->FilePointer;
1693     }
1694 
1695     /* Return the file object */
1696     return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
1697 }
1698 
1699 static
1700 PFILE_OBJECT
1701 MiGetFileObjectForVad(
1702     _In_ PMMVAD Vad)
1703 {
1704     PCONTROL_AREA ControlArea;
1705     PFILE_OBJECT FileObject;
1706 
1707     /* Check if this is a RosMm memory area */
1708     if (Vad->u.VadFlags.Spare != 0)
1709     {
1710         PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad;
1711         PROS_SECTION_OBJECT Section;
1712 
1713         /* Check if it's a section view (RosMm section) */
1714         if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1715         {
1716             /* Get the section pointer to the SECTION_OBJECT */
1717             Section = MemoryArea->Data.SectionData.Section;
1718             FileObject = Section->FileObject;
1719         }
1720         else
1721         {
1722             ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
1723             DPRINT1("VAD is a cache section!\n");
1724             return NULL;
1725         }
1726     }
1727     else
1728     {
1729         /* Make sure it's not a VM VAD */
1730         if (Vad->u.VadFlags.PrivateMemory == 1)
1731         {
1732             DPRINT1("VAD is not a section\n");
1733             return NULL;
1734         }
1735 
1736         /* Get the control area */
1737         ControlArea = Vad->ControlArea;
1738         if ((ControlArea == NULL) || !ControlArea->u.Flags.Image)
1739         {
1740             DPRINT1("Address is not a section\n");
1741             return NULL;
1742         }
1743 
1744         /* Get the file object */
1745         FileObject = ControlArea->FilePointer;
1746     }
1747 
1748     /* Return the file object */
1749     return FileObject;
1750 }
1751 
1752 VOID
1753 NTAPI
1754 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation)
1755 {
1756     PSECTION_OBJECT SectionObject;
1757 
1758     /* Get the section object of this process*/
1759     SectionObject = PsGetCurrentProcess()->SectionObject;
1760     ASSERT(SectionObject != NULL);
1761     ASSERT(MiIsRosSectionObject(SectionObject) == TRUE);
1762 
1763     /* Return the image information */
1764     *ImageInformation = ((PROS_SECTION_OBJECT)SectionObject)->ImageSection->ImageInformation;
1765 }
1766 
1767 NTSTATUS
1768 NTAPI
1769 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject,
1770                            OUT POBJECT_NAME_INFORMATION *ModuleName)
1771 {
1772     POBJECT_NAME_INFORMATION ObjectNameInfo;
1773     NTSTATUS Status;
1774     ULONG ReturnLength;
1775 
1776     /* Allocate memory for our structure */
1777     ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM);
1778     if (!ObjectNameInfo) return STATUS_NO_MEMORY;
1779 
1780     /* Query the name */
1781     Status = ObQueryNameString(FileObject,
1782                                ObjectNameInfo,
1783                                1024,
1784                                &ReturnLength);
1785     if (!NT_SUCCESS(Status))
1786     {
1787         /* Failed, free memory */
1788         DPRINT1("Name query failed\n");
1789         ExFreePoolWithTag(ObjectNameInfo, TAG_MM);
1790         *ModuleName = NULL;
1791         return Status;
1792     }
1793 
1794     /* Success */
1795     *ModuleName = ObjectNameInfo;
1796     return STATUS_SUCCESS;
1797 }
1798 
1799 NTSTATUS
1800 NTAPI
1801 MmGetFileNameForSection(IN PVOID Section,
1802                         OUT POBJECT_NAME_INFORMATION *ModuleName)
1803 {
1804     PFILE_OBJECT FileObject;
1805 
1806     /* Make sure it's an image section */
1807     if (MiIsRosSectionObject(Section) == FALSE)
1808     {
1809         /* Check ARM3 Section flag */
1810         if (((PSECTION)Section)->u.Flags.Image == 0)
1811         {
1812             /* It's not, fail */
1813             DPRINT1("Not an image section\n");
1814             return STATUS_SECTION_NOT_IMAGE;
1815         }
1816     }
1817     else if (!(((PROS_SECTION_OBJECT)Section)->AllocationAttributes & SEC_IMAGE))
1818     {
1819         /* It's not, fail */
1820         DPRINT1("Not an image section\n");
1821         return STATUS_SECTION_NOT_IMAGE;
1822     }
1823 
1824     /* Get the file object */
1825     FileObject = MmGetFileObjectForSection(Section);
1826     return MmGetFileNameForFileObject(FileObject, ModuleName);
1827 }
1828 
1829 NTSTATUS
1830 NTAPI
1831 MmGetFileNameForAddress(IN PVOID Address,
1832                         OUT PUNICODE_STRING ModuleName)
1833 {
1834     POBJECT_NAME_INFORMATION ModuleNameInformation;
1835     PVOID AddressSpace;
1836     NTSTATUS Status;
1837     PMMVAD Vad;
1838     PFILE_OBJECT FileObject = NULL;
1839 
1840     /* Lock address space */
1841     AddressSpace = MmGetCurrentAddressSpace();
1842     MmLockAddressSpace(AddressSpace);
1843 
1844     /* Get the VAD */
1845     Vad = MiLocateAddress(Address);
1846     if (Vad == NULL)
1847     {
1848         /* Fail, the address does not exist */
1849         DPRINT1("No VAD at address %p\n", Address);
1850         MmUnlockAddressSpace(AddressSpace);
1851         return STATUS_INVALID_ADDRESS;
1852     }
1853 
1854     /* Get the file object pointer for the VAD */
1855     FileObject = MiGetFileObjectForVad(Vad);
1856     if (FileObject == NULL)
1857     {
1858         DPRINT1("Failed to get file object for Address %p\n", Address);
1859         MmUnlockAddressSpace(AddressSpace);
1860         return STATUS_SECTION_NOT_IMAGE;
1861     }
1862 
1863     /* Reference the file object */
1864     ObReferenceObject(FileObject);
1865 
1866     /* Unlock address space */
1867     MmUnlockAddressSpace(AddressSpace);
1868 
1869     /* Get the filename of the file object */
1870     Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation);
1871 
1872     /* Dereference the file object */
1873     ObDereferenceObject(FileObject);
1874 
1875     /* Check if we were able to get the file object name */
1876     if (NT_SUCCESS(Status))
1877     {
1878         /* Init modulename */
1879         RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer);
1880 
1881         /* Free temp taged buffer from MmGetFileNameForFileObject() */
1882         ExFreePoolWithTag(ModuleNameInformation, TAG_MM);
1883         DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer, Address);
1884     }
1885 
1886    /* Return status */
1887    return Status;
1888 }
1889 
1890 NTSTATUS
1891 NTAPI
1892 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
1893                          IN PVOID BaseAddress,
1894                          OUT PVOID MemoryInformation,
1895                          IN SIZE_T MemoryInformationLength,
1896                          OUT PSIZE_T ReturnLength)
1897 {
1898     PEPROCESS Process;
1899     NTSTATUS Status;
1900     UNICODE_STRING ModuleFileName;
1901     PMEMORY_SECTION_NAME SectionName = NULL;
1902     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1903 
1904     Status = ObReferenceObjectByHandle(ProcessHandle,
1905                                        PROCESS_QUERY_INFORMATION,
1906                                        NULL,
1907                                        PreviousMode,
1908                                        (PVOID*)(&Process),
1909                                        NULL);
1910 
1911     if (!NT_SUCCESS(Status))
1912     {
1913         DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
1914         return Status;
1915     }
1916 
1917     Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
1918 
1919     if (NT_SUCCESS(Status))
1920     {
1921         SectionName = MemoryInformation;
1922         if (PreviousMode != KernelMode)
1923         {
1924             _SEH2_TRY
1925             {
1926                 RtlInitEmptyUnicodeString(&SectionName->SectionFileName,
1927                                           (PWSTR)(SectionName + 1),
1928                                           MemoryInformationLength - sizeof(MEMORY_SECTION_NAME));
1929                 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1930 
1931                 if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME);
1932 
1933             }
1934             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1935             {
1936                 Status = _SEH2_GetExceptionCode();
1937             }
1938             _SEH2_END;
1939         }
1940         else
1941         {
1942             RtlInitEmptyUnicodeString(&SectionName->SectionFileName,
1943                                       (PWSTR)(SectionName + 1),
1944                                       MemoryInformationLength - sizeof(MEMORY_SECTION_NAME));
1945             RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1946 
1947             if (ReturnLength) *ReturnLength = ModuleFileName.Length + sizeof(MEMORY_SECTION_NAME);
1948 
1949         }
1950 
1951         RtlFreeUnicodeString(&ModuleFileName);
1952     }
1953     ObDereferenceObject(Process);
1954     return Status;
1955 }
1956 
1957 VOID
1958 NTAPI
1959 MiFlushTbAndCapture(IN PMMVAD FoundVad,
1960                     IN PMMPTE PointerPte,
1961                     IN ULONG ProtectionMask,
1962                     IN PMMPFN Pfn1,
1963                     IN BOOLEAN UpdateDirty)
1964 {
1965     MMPTE TempPte, PreviousPte;
1966     KIRQL OldIrql;
1967     BOOLEAN RebuildPte = FALSE;
1968 
1969     //
1970     // User for sanity checking later on
1971     //
1972     PreviousPte = *PointerPte;
1973 
1974     //
1975     // Build the PTE and acquire the PFN lock
1976     //
1977     MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1978                               PointerPte,
1979                               ProtectionMask,
1980                               PreviousPte.u.Hard.PageFrameNumber);
1981     OldIrql = MiAcquirePfnLock();
1982 
1983     //
1984     // We don't support I/O mappings in this path yet
1985     //
1986     ASSERT(Pfn1 != NULL);
1987     ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined);
1988 
1989     //
1990     // Make sure new protection mask doesn't get in conflict and fix it if it does
1991     //
1992     if (Pfn1->u3.e1.CacheAttribute == MiCached)
1993     {
1994         //
1995         // This is a cached PFN
1996         //
1997         if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS))
1998         {
1999             RebuildPte = TRUE;
2000             ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS);
2001         }
2002     }
2003     else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
2004     {
2005         //
2006         // This is a non-cached PFN
2007         //
2008         if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE)
2009         {
2010             RebuildPte = TRUE;
2011             ProtectionMask &= ~MM_NOACCESS;
2012             ProtectionMask |= MM_NOCACHE;
2013         }
2014     }
2015 
2016     if (RebuildPte)
2017     {
2018         MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2019                                   PointerPte,
2020                                   ProtectionMask,
2021                                   PreviousPte.u.Hard.PageFrameNumber);
2022     }
2023 
2024     //
2025     // Write the new PTE, making sure we are only changing the bits
2026     //
2027     MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2028 
2029     //
2030     // Flush the TLB
2031     //
2032     ASSERT(PreviousPte.u.Hard.Valid == 1);
2033     KeFlushCurrentTb();
2034     ASSERT(PreviousPte.u.Hard.Valid == 1);
2035 
2036     //
2037     // Windows updates the relevant PFN1 information, we currently don't.
2038     //
2039     if (UpdateDirty && PreviousPte.u.Hard.Dirty)
2040     {
2041         if (!Pfn1->u3.e1.Modified)
2042         {
2043             DPRINT1("FIXME: Mark PFN as dirty\n");
2044         }
2045     }
2046 
2047     //
2048     // Not supported in ARM3
2049     //
2050     ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
2051 
2052     //
2053     // Release the PFN lock, we are done
2054     //
2055     MiReleasePfnLock(OldIrql);
2056 }
2057 
2058 //
2059 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
2060 //
2061 NTSTATUS
2062 NTAPI
2063 MiSetProtectionOnSection(IN PEPROCESS Process,
2064                          IN PMMVAD FoundVad,
2065                          IN PVOID StartingAddress,
2066                          IN PVOID EndingAddress,
2067                          IN ULONG NewProtect,
2068                          OUT PULONG CapturedOldProtect,
2069                          IN ULONG DontCharge,
2070                          OUT PULONG Locked)
2071 {
2072     PMMPTE PointerPte, LastPte;
2073     MMPTE TempPte, PteContents;
2074     PMMPDE PointerPde;
2075     PMMPFN Pfn1;
2076     ULONG ProtectionMask, QuotaCharge = 0;
2077     PETHREAD Thread = PsGetCurrentThread();
2078     PAGED_CODE();
2079 
2080     //
2081     // Tell caller nothing is being locked
2082     //
2083     *Locked = FALSE;
2084 
2085     //
2086     // This function should only be used for section VADs. Windows ASSERT */
2087     //
2088     ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
2089 
2090     //
2091     // We don't support these features in ARM3
2092     //
2093     ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2094     ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
2095 
2096     //
2097     // Convert and validate the protection mask
2098     //
2099     ProtectionMask = MiMakeProtectionMask(NewProtect);
2100     if (ProtectionMask == MM_INVALID_PROTECTION)
2101     {
2102         DPRINT1("Invalid section protect\n");
2103         return STATUS_INVALID_PAGE_PROTECTION;
2104     }
2105 
2106     //
2107     // Get the PTE and PDE for the address, as well as the final PTE
2108     //
2109     MiLockProcessWorkingSetUnsafe(Process, Thread);
2110     PointerPde = MiAddressToPde(StartingAddress);
2111     PointerPte = MiAddressToPte(StartingAddress);
2112     LastPte = MiAddressToPte(EndingAddress);
2113 
2114     //
2115     // Make the PDE valid, and check the status of the first PTE
2116     //
2117     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2118     if (PointerPte->u.Long)
2119     {
2120         //
2121         // Not supported in ARM3
2122         //
2123         ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2124 
2125         //
2126         // Capture the page protection and make the PDE valid
2127         //
2128         *CapturedOldProtect = MiGetPageProtection(PointerPte);
2129         MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2130     }
2131     else
2132     {
2133         //
2134         // Only pagefile-backed section VADs are supported for now
2135         //
2136         ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2137 
2138         //
2139         // Grab the old protection from the VAD itself
2140         //
2141         *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2142     }
2143 
2144     //
2145     // Loop all the PTEs now
2146     //
2147     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2148     while (PointerPte <= LastPte)
2149     {
2150         //
2151         // Check if we've crossed a PDE boundary and make the new PDE valid too
2152         //
2153         if (MiIsPteOnPdeBoundary(PointerPte))
2154         {
2155             PointerPde = MiPteToPde(PointerPte);
2156             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2157         }
2158 
2159         //
2160         // Capture the PTE and see what we're dealing with
2161         //
2162         PteContents = *PointerPte;
2163         if (PteContents.u.Long == 0)
2164         {
2165             //
2166             // This used to be a zero PTE and it no longer is, so we must add a
2167             // reference to the pagetable.
2168             //
2169             MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2170 
2171             //
2172             // Create the demand-zero prototype PTE
2173             //
2174             TempPte = PrototypePte;
2175             TempPte.u.Soft.Protection = ProtectionMask;
2176             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2177         }
2178         else if (PteContents.u.Hard.Valid == 1)
2179         {
2180             //
2181             // Get the PFN entry
2182             //
2183             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2184 
2185             //
2186             // We don't support these yet
2187             //
2188             ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2189             ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2190 
2191             //
2192             // Write the protection mask and write it with a TLB flush
2193             //
2194             Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2195             MiFlushTbAndCapture(FoundVad,
2196                                 PointerPte,
2197                                 ProtectionMask,
2198                                 Pfn1,
2199                                 TRUE);
2200         }
2201         else
2202         {
2203             //
2204             // We don't support these cases yet
2205             //
2206             ASSERT(PteContents.u.Soft.Prototype == 0);
2207             ASSERT(PteContents.u.Soft.Transition == 0);
2208 
2209             //
2210             // The PTE is already demand-zero, just update the protection mask
2211             //
2212             PointerPte->u.Soft.Protection = ProtectionMask;
2213         }
2214 
2215         PointerPte++;
2216     }
2217 
2218     //
2219     // Unlock the working set and update quota charges if needed, then return
2220     //
2221     MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2222     if ((QuotaCharge > 0) && (!DontCharge))
2223     {
2224         FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2225         Process->CommitCharge -= QuotaCharge;
2226     }
2227     return STATUS_SUCCESS;
2228 }
2229 
2230 VOID
2231 NTAPI
2232 MiRemoveMappedPtes(IN PVOID BaseAddress,
2233                    IN ULONG NumberOfPtes,
2234                    IN PCONTROL_AREA ControlArea,
2235                    IN PMMSUPPORT Ws)
2236 {
2237     PMMPTE PointerPte, ProtoPte;//, FirstPte;
2238     PMMPDE PointerPde, SystemMapPde;
2239     PMMPFN Pfn1, Pfn2;
2240     MMPTE PteContents;
2241     KIRQL OldIrql;
2242     DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2243 
2244     ASSERT(Ws == NULL);
2245 
2246     /* Get the PTE and loop each one */
2247     PointerPte = MiAddressToPte(BaseAddress);
2248     //FirstPte = PointerPte;
2249     while (NumberOfPtes)
2250     {
2251         /* Check if the PTE is already valid */
2252         PteContents = *PointerPte;
2253         if (PteContents.u.Hard.Valid == 1)
2254         {
2255             /* Get the PFN entry */
2256             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2257 
2258             /* Get the PTE */
2259             PointerPde = MiPteToPde(PointerPte);
2260 
2261             /* Lock the PFN database and make sure this isn't a mapped file */
2262             OldIrql = MiAcquirePfnLock();
2263             ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2264 
2265             /* Mark the page as modified accordingly */
2266             if (MI_IS_PAGE_DIRTY(&PteContents))
2267                 Pfn1->u3.e1.Modified = 1;
2268 
2269             /* Was the PDE invalid */
2270             if (PointerPde->u.Long == 0)
2271             {
2272 #if (_MI_PAGING_LEVELS == 2)
2273                 /* Find the system double-mapped PDE that describes this mapping */
2274                 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2275 
2276                 /* Make it valid */
2277                 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2278                 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2279 #else
2280                 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde);
2281                 ASSERT(FALSE);
2282 #endif
2283             }
2284 
2285             /* Dereference the PDE and the PTE */
2286             Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2287             MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2288             DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2289             MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2290 
2291             /* Release the PFN lock */
2292             MiReleasePfnLock(OldIrql);
2293         }
2294         else
2295         {
2296             /* Windows ASSERT */
2297             ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2298 
2299             /* Check if this is a prototype pointer PTE */
2300             if (PteContents.u.Soft.Prototype == 1)
2301             {
2302                 /* Get the prototype PTE */
2303                 ProtoPte = MiProtoPteToPte(&PteContents);
2304 
2305                 /* We don't support anything else atm */
2306                 ASSERT(ProtoPte->u.Long == 0);
2307             }
2308         }
2309 
2310         /* Make the PTE into a zero PTE */
2311         PointerPte->u.Long = 0;
2312 
2313         /* Move to the next PTE */
2314         PointerPte++;
2315         NumberOfPtes--;
2316     }
2317 
2318     /* Flush the TLB */
2319     KeFlushCurrentTb();
2320 
2321     /* Acquire the PFN lock */
2322     OldIrql = MiAcquirePfnLock();
2323 
2324     /* Decrement the accounting counters */
2325     ControlArea->NumberOfUserReferences--;
2326     ControlArea->NumberOfMappedViews--;
2327 
2328     /* Check if we should destroy the CA and release the lock */
2329     MiCheckControlArea(ControlArea, OldIrql);
2330 }
2331 
2332 ULONG
2333 NTAPI
2334 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2335                         IN PVOID Base,
2336                         OUT PCONTROL_AREA *ControlArea)
2337 {
2338     ULONG Hash, Size, Count = 0;
2339     ULONG_PTR Entry;
2340     PAGED_CODE();
2341 
2342     /* Compute the hash for this entry and loop trying to find it */
2343     Entry = (ULONG_PTR)Base >> 16;
2344     Hash = Entry % Session->SystemSpaceHashKey;
2345     while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2346     {
2347         /* Check if we overflew past the end of the hash table */
2348         if (++Hash >= Session->SystemSpaceHashSize)
2349         {
2350             /* Reset the hash to zero and keep searching from the bottom */
2351             Hash = 0;
2352             if (++Count == 2)
2353             {
2354                 /* But if we overflew twice, then this is not a real mapping */
2355                 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2356                              (ULONG_PTR)Base,
2357                              1,
2358                              0,
2359                              0);
2360             }
2361         }
2362     }
2363 
2364     /* One less entry */
2365     Session->SystemSpaceHashEntries--;
2366 
2367     /* Extract the size and clear the entry */
2368     Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2369     Session->SystemSpaceViewTable[Hash].Entry = 0;
2370 
2371     /* Return the control area and the size */
2372     *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2373     return Size;
2374 }
2375 
2376 NTSTATUS
2377 NTAPI
2378 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2379                          IN PVOID MappedBase)
2380 {
2381     ULONG Size;
2382     PCONTROL_AREA ControlArea;
2383     PAGED_CODE();
2384 
2385     /* Remove this mapping */
2386     KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2387     Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2388 
2389     /* Clear the bits for this mapping */
2390     RtlClearBits(Session->SystemSpaceBitMap,
2391                  (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2392                  Size);
2393 
2394     /* Convert the size from a bit size into the actual size */
2395     Size = Size * (_64K >> PAGE_SHIFT);
2396 
2397     /* Remove the PTEs now */
2398     MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2399     KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2400 
2401     /* Return success */
2402     return STATUS_SUCCESS;
2403 }
2404 
2405 /* PUBLIC FUNCTIONS ***********************************************************/
2406 
2407 /*
2408  * @implemented
2409  */
2410 NTSTATUS
2411 NTAPI
2412 MmCreateArm3Section(OUT PVOID *SectionObject,
2413                     IN ACCESS_MASK DesiredAccess,
2414                     IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2415                     IN PLARGE_INTEGER InputMaximumSize,
2416                     IN ULONG SectionPageProtection,
2417                     IN ULONG AllocationAttributes,
2418                     IN HANDLE FileHandle OPTIONAL,
2419                     IN PFILE_OBJECT FileObject OPTIONAL)
2420 {
2421     SECTION Section;
2422     PSECTION NewSection;
2423     PSUBSECTION Subsection;
2424     PSEGMENT NewSegment, Segment;
2425     NTSTATUS Status;
2426     PCONTROL_AREA ControlArea;
2427     ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2428     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2429     BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2430     KIRQL OldIrql;
2431     PFILE_OBJECT File;
2432     BOOLEAN UserRefIncremented = FALSE;
2433     PVOID PreviousSectionPointer;
2434 
2435     /* Make the same sanity checks that the Nt interface should've validated */
2436     ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2437                                      SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2438                                      SEC_NO_CHANGE)) == 0);
2439     ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2440     ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2441              (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2442                                       SEC_NOCACHE | SEC_NO_CHANGE))));
2443     ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2444     ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2445              (SectionPageProtection & PAGE_WRITECOMBINE) ||
2446              (SectionPageProtection & PAGE_GUARD) ||
2447              (SectionPageProtection & PAGE_NOACCESS)));
2448 
2449     /* Convert section flag to page flag */
2450     if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2451 
2452     /* Check to make sure the protection is correct. Nt* does this already */
2453     ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2454     if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2455 
2456     /* Check if this is going to be a data or image backed file section */
2457     if ((FileHandle) || (FileObject))
2458     {
2459         /* These cannot be mapped with large pages */
2460         if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2461 
2462         /* For now, only support the mechanism through a file handle */
2463         ASSERT(FileObject == NULL);
2464 
2465         /* Reference the file handle to get the object */
2466         Status = ObReferenceObjectByHandle(FileHandle,
2467                                            MmMakeFileAccess[ProtectionMask],
2468                                            IoFileObjectType,
2469                                            PreviousMode,
2470                                            (PVOID*)&File,
2471                                            NULL);
2472         if (!NT_SUCCESS(Status)) return Status;
2473 
2474         /* Make sure Cc has been doing its job */
2475         if (!File->SectionObjectPointer)
2476         {
2477             /* This is not a valid file system-based file, fail */
2478             ObDereferenceObject(File);
2479             return STATUS_INVALID_FILE_FOR_SECTION;
2480         }
2481 
2482         /* Image-file backed sections are not yet supported */
2483         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2484 
2485         /* Compute the size of the control area, and allocate it */
2486         ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2487         ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2488         if (!ControlArea)
2489         {
2490             ObDereferenceObject(File);
2491             return STATUS_INSUFFICIENT_RESOURCES;
2492         }
2493 
2494         /* Zero it out */
2495         RtlZeroMemory(ControlArea, ControlAreaSize);
2496 
2497         /* Did we get a handle, or an object? */
2498         if (FileHandle)
2499         {
2500             /* We got a file handle so we have to lock down the file */
2501 #if 0
2502             Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2503             if (!NT_SUCCESS(Status))
2504             {
2505                 ExFreePool(ControlArea);
2506                 ObDereferenceObject(File);
2507                 return Status;
2508             }
2509 #else
2510             /* ReactOS doesn't support this API yet, so do nothing */
2511             Status = STATUS_SUCCESS;
2512 #endif
2513             /* Update the top-level IRP so that drivers know what's happening */
2514             IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2515             FileLock = TRUE;
2516         }
2517 
2518         /* Lock the PFN database while we play with the section pointers */
2519         OldIrql = MiAcquirePfnLock();
2520 
2521         /* Image-file backed sections are not yet supported */
2522         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2523 
2524         /* There should not already be a control area for this file */
2525         ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2526         NewSegment = NULL;
2527 
2528         /* Write down that this CA is being created, and set it */
2529         ControlArea->u.Flags.BeingCreated = TRUE;
2530         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2531         PreviousSectionPointer = File->SectionObjectPointer;
2532         File->SectionObjectPointer->DataSectionObject = ControlArea;
2533 
2534         /* We can release the PFN lock now */
2535         MiReleasePfnLock(OldIrql);
2536 
2537         /* We don't support previously-mapped file */
2538         ASSERT(NewSegment == NULL);
2539 
2540         /* Image-file backed sections are not yet supported */
2541         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2542 
2543         /* So we always create a data file map */
2544         Status = MiCreateDataFileMap(File,
2545                                      &Segment,
2546                                      (PSIZE_T)InputMaximumSize,
2547                                      SectionPageProtection,
2548                                      AllocationAttributes,
2549                                      KernelCall);
2550         if (!NT_SUCCESS(Status))
2551         {
2552             /* Lock the PFN database while we play with the section pointers */
2553             OldIrql = MiAcquirePfnLock();
2554 
2555             /* Reset the waiting-for-deletion event */
2556             ASSERT(ControlArea->WaitingForDeletion == NULL);
2557             ControlArea->WaitingForDeletion = NULL;
2558 
2559             /* Set the file pointer NULL flag */
2560             ASSERT(ControlArea->u.Flags.FilePointerNull == 0);
2561             ControlArea->u.Flags.FilePointerNull = TRUE;
2562 
2563             /* Delete the data section object */
2564             ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2565             File->SectionObjectPointer->DataSectionObject = NULL;
2566 
2567             /* No longer being created */
2568             ControlArea->u.Flags.BeingCreated = FALSE;
2569 
2570             /* We can release the PFN lock now */
2571             MiReleasePfnLock(OldIrql);
2572 
2573             /* Check if we locked and set the IRP */
2574             if (FileLock)
2575             {
2576                 /* Undo */
2577                 IoSetTopLevelIrp(NULL);
2578                 //FsRtlReleaseFile(File);
2579             }
2580 
2581             /* Free the control area and de-ref the file object */
2582             ExFreePool(ControlArea);
2583             ObDereferenceObject(File);
2584 
2585             /* All done */
2586             return Status;
2587         }
2588 
2589         /* On success, we expect this */
2590         ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2591 
2592         /* Check if a maximum size was specified */
2593         if (!InputMaximumSize->QuadPart)
2594         {
2595             /* Nope, use the segment size */
2596             Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2597         }
2598         else
2599         {
2600             /* Yep, use the entered size */
2601             Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2602         }
2603     }
2604     else
2605     {
2606         /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2607         if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2608 
2609         /* Not yet supported */
2610         ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2611 
2612         /* So this must be a pagefile-backed section, create the mappings needed */
2613         Status = MiCreatePagingFileMap(&NewSegment,
2614                                        (PSIZE_T)InputMaximumSize,
2615                                        ProtectionMask,
2616                                        AllocationAttributes);
2617         if (!NT_SUCCESS(Status)) return Status;
2618 
2619         /* Set the size here, and read the control area */
2620         Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2621         ControlArea = NewSegment->ControlArea;
2622 
2623         /* MiCreatePagingFileMap increments user references */
2624         UserRefIncremented = TRUE;
2625     }
2626 
2627     /* Did we already have a segment? */
2628     if (!NewSegment)
2629     {
2630         /* This must be the file path and we created a segment */
2631         NewSegment = Segment;
2632         ASSERT(File != NULL);
2633 
2634         /* Acquire the PFN lock while we set control area flags */
2635         OldIrql = MiAcquirePfnLock();
2636 
2637         /* We don't support this race condition yet, so assume no waiters */
2638         ASSERT(ControlArea->WaitingForDeletion == NULL);
2639         ControlArea->WaitingForDeletion = NULL;
2640 
2641         /* Image-file backed sections are not yet supported, nor ROM images */
2642         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2643         ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2644 
2645         /* Take off the being created flag, and then release the lock */
2646         ControlArea->u.Flags.BeingCreated = FALSE;
2647         MiReleasePfnLock(OldIrql);
2648     }
2649 
2650     /* Check if we locked the file earlier */
2651     if (FileLock)
2652     {
2653         /* Reset the top-level IRP and release the lock */
2654         IoSetTopLevelIrp(NULL);
2655         //FsRtlReleaseFile(File);
2656         FileLock = FALSE;
2657     }
2658 
2659     /* Set the initial section object data */
2660     Section.InitialPageProtection = SectionPageProtection;
2661 
2662     /* The mapping created a control area and segment, save the flags */
2663     Section.Segment = NewSegment;
2664     Section.u.LongFlags = ControlArea->u.LongFlags;
2665 
2666     /* Check if this is a user-mode read-write non-image file mapping */
2667     if (!(FileObject) &&
2668         (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2669         !(ControlArea->u.Flags.Image) &&
2670         (ControlArea->FilePointer))
2671     {
2672         /* Add a reference and set the flag */
2673         Section.u.Flags.UserWritable = TRUE;
2674         InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences);
2675     }
2676 
2677     /* Check for image mappings or page file mappings */
2678     if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer))
2679     {
2680         /* Charge the segment size, and allocate a subsection */
2681         PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2682         Size = sizeof(SUBSECTION);
2683     }
2684     else
2685     {
2686         /* Charge nothing, and allocate a mapped subsection */
2687         PagedCharge = 0;
2688         Size = sizeof(MSUBSECTION);
2689     }
2690 
2691     /* Check if this is a normal CA */
2692     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2693     ASSERT(ControlArea->u.Flags.Rom == 0);
2694 
2695     /* Charge only a CA, and the subsection is right after */
2696     NonPagedCharge = sizeof(CONTROL_AREA);
2697     Subsection = (PSUBSECTION)(ControlArea + 1);
2698 
2699     /* We only support single-subsection mappings */
2700     NonPagedCharge += Size;
2701     ASSERT(Subsection->NextSubsection == NULL);
2702 
2703     /* Create the actual section object, with enough space for the prototype PTEs */
2704     Status = ObCreateObject(PreviousMode,
2705                             MmSectionObjectType,
2706                             ObjectAttributes,
2707                             PreviousMode,
2708                             NULL,
2709                             sizeof(SECTION),
2710                             PagedCharge,
2711                             NonPagedCharge,
2712                             (PVOID*)&NewSection);
2713     if (!NT_SUCCESS(Status))
2714     {
2715         /* Check if this is a user-mode read-write non-image file mapping */
2716         if (!(FileObject) &&
2717             (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2718             !(ControlArea->u.Flags.Image) &&
2719             (ControlArea->FilePointer))
2720         {
2721             /* Remove a reference and check the flag */
2722             ASSERT(Section.u.Flags.UserWritable == 1);
2723             InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences);
2724         }
2725 
2726         /* Check if a user reference was added */
2727         if (UserRefIncremented)
2728         {
2729             /* Acquire the PFN lock while we change counters */
2730             OldIrql = MiAcquirePfnLock();
2731 
2732             /* Decrement the accounting counters */
2733             ControlArea->NumberOfSectionReferences--;
2734             ASSERT((LONG)ControlArea->NumberOfUserReferences > 0);
2735             ControlArea->NumberOfUserReferences--;
2736 
2737             /* Check if we should destroy the CA and release the lock */
2738             MiCheckControlArea(ControlArea, OldIrql);
2739         }
2740 
2741         /* Return the failure code */
2742         return Status;
2743     }
2744 
2745     /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */
2746 
2747     /* Now copy the local section object from the stack into this new object */
2748     RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2749     NewSection->Address.StartingVpn = 0;
2750 
2751     /* For now, only user calls are supported */
2752     ASSERT(KernelCall == FALSE);
2753     NewSection->u.Flags.UserReference = TRUE;
2754 
2755     /* Is this a "based" allocation, in which all mappings are identical? */
2756     if (AllocationAttributes & SEC_BASED)
2757     {
2758         /* Lock the VAD tree during the search */
2759         KeAcquireGuardedMutex(&MmSectionBasedMutex);
2760 
2761         /* Is it a brand new ControArea ? */
2762         if (ControlArea->u.Flags.BeingCreated == 1)
2763         {
2764             ASSERT(ControlArea->u.Flags.Based == 1);
2765             /* Then we must find a global address, top-down */
2766             Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment,
2767                                                           (ULONG_PTR)MmHighSectionBase,
2768                                                           _64K,
2769                                                           &MmSectionBasedRoot,
2770                                                           (ULONG_PTR*)&ControlArea->Segment->BasedAddress);
2771 
2772             if (!NT_SUCCESS(Status))
2773             {
2774                 /* No way to find a valid range. */
2775                 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2776                 ControlArea->u.Flags.Based = 0;
2777                 NewSection->u.Flags.Based = 0;
2778                 ObDereferenceObject(NewSection);
2779                 return Status;
2780             }
2781 
2782             /* Compute the ending address and insert it into the VAD tree */
2783             NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress;
2784             NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1;
2785             MiInsertBasedSection(NewSection);
2786         }
2787         else
2788         {
2789             /* FIXME : Should we deny section creation if SEC_BASED is not set ? Can we have two different section objects on the same based address ? Investigate !*/
2790             ASSERT(FALSE);
2791         }
2792 
2793         KeReleaseGuardedMutex(&MmSectionBasedMutex);
2794     }
2795 
2796     /* The control area is not being created anymore */
2797     if (ControlArea->u.Flags.BeingCreated == 1)
2798     {
2799         /* Acquire the PFN lock while we set control area flags */
2800         OldIrql = MiAcquirePfnLock();
2801 
2802         /* Take off the being created flag, and then release the lock */
2803         ControlArea->u.Flags.BeingCreated = 0;
2804         NewSection->u.Flags.BeingCreated = 0;
2805 
2806         MiReleasePfnLock(OldIrql);
2807     }
2808 
2809     /* Migrate the attribute into a flag */
2810     if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2811 
2812     /* If R/W access is not requested, this might eventually become a CoW mapping */
2813     if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2814     {
2815         NewSection->u.Flags.CopyOnWrite = TRUE;
2816     }
2817 
2818     /* Write down if this was a kernel call */
2819     ControlArea->u.Flags.WasPurged |= KernelCall;
2820     ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2821 
2822     /* Make sure the segment and the section are the same size, or the section is smaller */
2823     ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2824 
2825     /* Return the object and the creation status */
2826     *SectionObject = (PVOID)NewSection;
2827     return Status;
2828 }
2829 
2830 /*
2831  * @implemented
2832  */
2833 NTSTATUS
2834 NTAPI
2835 MmMapViewOfArm3Section(IN PVOID SectionObject,
2836                        IN PEPROCESS Process,
2837                        IN OUT PVOID *BaseAddress,
2838                        IN ULONG_PTR ZeroBits,
2839                        IN SIZE_T CommitSize,
2840                        IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2841                        IN OUT PSIZE_T ViewSize,
2842                        IN SECTION_INHERIT InheritDisposition,
2843                        IN ULONG AllocationType,
2844                        IN ULONG Protect)
2845 {
2846     KAPC_STATE ApcState;
2847     BOOLEAN Attached = FALSE;
2848     PSECTION Section;
2849     PCONTROL_AREA ControlArea;
2850     ULONG ProtectionMask;
2851     NTSTATUS Status;
2852     ULONG64 CalculatedViewSize;
2853     PAGED_CODE();
2854 
2855     /* Get the segment and control area */
2856     Section = (PSECTION)SectionObject;
2857     ControlArea = Section->Segment->ControlArea;
2858 
2859     /* These flags/states are not yet supported by ARM3 */
2860     ASSERT(Section->u.Flags.Image == 0);
2861     ASSERT(Section->u.Flags.NoCache == 0);
2862     ASSERT(Section->u.Flags.WriteCombined == 0);
2863     ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2864 
2865     /* FIXME */
2866     if ((AllocationType & MEM_RESERVE) != 0)
2867     {
2868         DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2869         return STATUS_NOT_IMPLEMENTED;
2870     }
2871 
2872     /* Check if the mapping protection is compatible with the create */
2873     if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2874     {
2875         DPRINT1("Mapping protection is incompatible\n");
2876         return STATUS_SECTION_PROTECTION;
2877     }
2878 
2879     /* Check if the offset and size would cause an overflow */
2880     if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2881          (ULONG64)SectionOffset->QuadPart)
2882     {
2883         DPRINT1("Section offset overflows\n");
2884         return STATUS_INVALID_VIEW_SIZE;
2885     }
2886 
2887     /* Check if the offset and size are bigger than the section itself */
2888     if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2889          (ULONG64)Section->SizeOfSection.QuadPart)
2890     {
2891         DPRINT1("Section offset is larger than section\n");
2892         return STATUS_INVALID_VIEW_SIZE;
2893     }
2894 
2895     /* Check if the caller did not specify a view size */
2896     if (!(*ViewSize))
2897     {
2898         /* Compute it for the caller */
2899         CalculatedViewSize = Section->SizeOfSection.QuadPart -
2900                              SectionOffset->QuadPart;
2901 
2902         /* Check if it's larger than 4GB or overflows into kernel-mode */
2903         if (!NT_SUCCESS(RtlULongLongToSIZET(CalculatedViewSize, ViewSize)) ||
2904             (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < CalculatedViewSize))
2905         {
2906             DPRINT1("Section view won't fit\n");
2907             return STATUS_INVALID_VIEW_SIZE;
2908         }
2909     }
2910 
2911     /* Check if the commit size is larger than the view size */
2912     if (CommitSize > *ViewSize)
2913     {
2914         DPRINT1("Attempting to commit more than the view itself\n");
2915         return STATUS_INVALID_PARAMETER_5;
2916     }
2917 
2918     /* Check if the view size is larger than the section */
2919     if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2920     {
2921         DPRINT1("The view is larger than the section\n");
2922         return STATUS_INVALID_VIEW_SIZE;
2923     }
2924 
2925     /* Compute and validate the protection mask */
2926     ProtectionMask = MiMakeProtectionMask(Protect);
2927     if (ProtectionMask == MM_INVALID_PROTECTION)
2928     {
2929         DPRINT1("The protection is invalid\n");
2930         return STATUS_INVALID_PAGE_PROTECTION;
2931     }
2932 
2933     /* We only handle pagefile-backed sections, which cannot be writecombined */
2934     if (Protect & PAGE_WRITECOMBINE)
2935     {
2936         DPRINT1("Cannot write combine a pagefile-backed section\n");
2937         return STATUS_INVALID_PARAMETER_10;
2938     }
2939 
2940     /* Start by attaching to the current process if needed */
2941     if (PsGetCurrentProcess() != Process)
2942     {
2943         KeStackAttachProcess(&Process->Pcb, &ApcState);
2944         Attached = TRUE;
2945     }
2946 
2947     /* Do the actual mapping */
2948     Status = MiMapViewOfDataSection(ControlArea,
2949                                     Process,
2950                                     BaseAddress,
2951                                     SectionOffset,
2952                                     ViewSize,
2953                                     Section,
2954                                     InheritDisposition,
2955                                     ProtectionMask,
2956                                     CommitSize,
2957                                     ZeroBits,
2958                                     AllocationType);
2959 
2960     /* Detach if needed, then return status */
2961     if (Attached) KeUnstackDetachProcess(&ApcState);
2962     return Status;
2963 }
2964 
2965 /*
2966  * @unimplemented
2967  */
2968 BOOLEAN
2969 NTAPI
2970 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2971 {
2972    UNIMPLEMENTED;
2973    return FALSE;
2974 }
2975 
2976 /*
2977  * @unimplemented
2978  */
2979 BOOLEAN
2980 NTAPI
2981 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2982                      IN BOOLEAN DelayClose)
2983 {
2984    UNIMPLEMENTED;
2985    return FALSE;
2986 }
2987 
2988 /*
2989  * @implemented
2990  */
2991 NTSTATUS
2992 NTAPI
2993 MmMapViewInSessionSpace(IN PVOID Section,
2994                         OUT PVOID *MappedBase,
2995                         IN OUT PSIZE_T ViewSize)
2996 {
2997     PAGED_CODE();
2998 
2999     // HACK
3000     if (MiIsRosSectionObject(Section))
3001     {
3002         return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
3003     }
3004 
3005     /* Process must be in a session */
3006     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3007     {
3008         DPRINT1("Process is not in session\n");
3009         return STATUS_NOT_MAPPED_VIEW;
3010     }
3011 
3012     /* Use the system space API, but with the session view instead */
3013     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3014     return MiMapViewInSystemSpace(Section,
3015                                   &MmSessionSpace->Session,
3016                                   MappedBase,
3017                                   ViewSize);
3018 }
3019 
3020 /*
3021  * @implemented
3022  */
3023 NTSTATUS
3024 NTAPI
3025 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
3026 {
3027     PAGED_CODE();
3028 
3029     // HACK
3030     if (!MI_IS_SESSION_ADDRESS(MappedBase))
3031     {
3032         return MmUnmapViewInSystemSpace(MappedBase);
3033     }
3034 
3035     /* Process must be in a session */
3036     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3037     {
3038         DPRINT1("Proess is not in session\n");
3039         return STATUS_NOT_MAPPED_VIEW;
3040     }
3041 
3042     /* Use the system space API, but with the session view instead */
3043     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3044     return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
3045                                     MappedBase);
3046 }
3047 
3048 /*
3049  * @implemented
3050  */
3051 NTSTATUS
3052 NTAPI
3053 MmUnmapViewOfSection(IN PEPROCESS Process,
3054                      IN PVOID BaseAddress)
3055 {
3056     return MiUnmapViewOfSection(Process, BaseAddress, 0);
3057 }
3058 
3059 /*
3060  * @implemented
3061  */
3062 NTSTATUS
3063 NTAPI
3064 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
3065 {
3066     PMEMORY_AREA MemoryArea;
3067     PAGED_CODE();
3068 
3069     /* Was this mapped by RosMm? */
3070     MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
3071     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
3072     {
3073         return MiRosUnmapViewInSystemSpace(MappedBase);
3074     }
3075 
3076     /* It was not, call the ARM3 routine */
3077     return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
3078 }
3079 
3080 /*
3081  * @implemented
3082  */
3083 NTSTATUS
3084 NTAPI
3085 MmCommitSessionMappedView(IN PVOID MappedBase,
3086                           IN SIZE_T ViewSize)
3087 {
3088     ULONG_PTR StartAddress, EndingAddress, Base;
3089     ULONG Hash, Count = 0, Size, QuotaCharge;
3090     PMMSESSION Session;
3091     PMMPTE LastProtoPte, PointerPte, ProtoPte;
3092     PCONTROL_AREA ControlArea;
3093     PSEGMENT Segment;
3094     PSUBSECTION Subsection;
3095     MMPTE TempPte;
3096     PAGED_CODE();
3097 
3098     /* Make sure the base isn't past the session view range */
3099     if ((MappedBase < MiSessionViewStart) ||
3100         (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
3101     {
3102         DPRINT1("Base outside of valid range\n");
3103         return STATUS_INVALID_PARAMETER_1;
3104     }
3105 
3106     /* Make sure the size isn't past the session view range */
3107     if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
3108         (ULONG_PTR)MappedBase) < ViewSize)
3109     {
3110         DPRINT1("Size outside of valid range\n");
3111         return STATUS_INVALID_PARAMETER_2;
3112     }
3113 
3114     /* Sanity check */
3115     ASSERT(ViewSize != 0);
3116 
3117     /* Process must be in a session */
3118     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3119     {
3120         DPRINT1("Process is not in session\n");
3121         return STATUS_NOT_MAPPED_VIEW;
3122     }
3123 
3124     /* Compute the correctly aligned base and end addresses */
3125     StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
3126     EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
3127 
3128     /* Sanity check and grab the session */
3129     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3130     Session = &MmSessionSpace->Session;
3131 
3132     /* Get the hash entry for this allocation */
3133     Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
3134 
3135     /* Lock system space */
3136     KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
3137 
3138     /* Loop twice so we can try rolling over if needed */
3139     while (TRUE)
3140     {
3141         /* Extract the size and base addresses from the entry */
3142         Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
3143         Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
3144 
3145         /* Convert the size to bucket chunks */
3146         Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
3147 
3148         /* Bail out if this entry fits in here */
3149         if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
3150 
3151         /* Check if we overflew past the end of the hash table */
3152         if (++Hash >= Session->SystemSpaceHashSize)
3153         {
3154             /* Reset the hash to zero and keep searching from the bottom */
3155             Hash = 0;
3156             if (++Count == 2)
3157             {
3158                 /* But if we overflew twice, then this is not a real mapping */
3159                 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
3160                              Base,
3161                              2,
3162                              0,
3163                              0);
3164             }
3165         }
3166     }
3167 
3168     /* Make sure the view being mapped is not file-based */
3169     ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
3170     if (ControlArea->FilePointer != NULL)
3171     {
3172         /* It is, so we have to bail out */
3173         DPRINT1("Only page-filed backed sections can be commited\n");
3174         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3175         return STATUS_ALREADY_COMMITTED;
3176     }
3177 
3178     /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
3179     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
3180     ASSERT(ControlArea->u.Flags.Rom == 0);
3181     Subsection = (PSUBSECTION)(ControlArea + 1);
3182 
3183     /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
3184     ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
3185     QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
3186     LastProtoPte = ProtoPte + QuotaCharge;
3187     if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
3188     {
3189         DPRINT1("PTE is out of bounds\n");
3190         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3191         return STATUS_INVALID_PARAMETER_2;
3192     }
3193 
3194     /* Acquire the commit lock and count all the non-committed PTEs */
3195     KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
3196     PointerPte = ProtoPte;
3197     while (PointerPte < LastProtoPte)
3198     {
3199         if (PointerPte->u.Long) QuotaCharge--;
3200         PointerPte++;
3201     }
3202 
3203     /* Was everything committed already? */
3204     if (!QuotaCharge)
3205     {
3206         /* Nothing to do! */
3207         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3208         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3209         return STATUS_SUCCESS;
3210     }
3211 
3212     /* Pick the segment and template PTE */
3213     Segment = ControlArea->Segment;
3214     TempPte = Segment->SegmentPteTemplate;
3215     ASSERT(TempPte.u.Long != 0);
3216 
3217     /* Loop all prototype PTEs to be committed */
3218     PointerPte = ProtoPte;
3219     while (PointerPte < LastProtoPte)
3220     {
3221         /* Make sure the PTE is already invalid */
3222         if (PointerPte->u.Long == 0)
3223         {
3224             /* And write the invalid PTE */
3225             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3226         }
3227 
3228         /* Move to the next PTE */
3229         PointerPte++;
3230     }
3231 
3232     /* Check if we had at least one page charged */
3233     if (QuotaCharge)
3234     {
3235         /* Update the accounting data */
3236         Segment->NumberOfCommittedPages += QuotaCharge;
3237         InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3238     }
3239 
3240     /* Release all */
3241     KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3242     KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3243     return STATUS_SUCCESS;
3244 }
3245 
3246 VOID
3247 NTAPI
3248 MiDeleteARM3Section(PVOID ObjectBody)
3249 {
3250     PSECTION SectionObject;
3251     PCONTROL_AREA ControlArea;
3252     KIRQL OldIrql;
3253 
3254     SectionObject = (PSECTION)ObjectBody;
3255 
3256     if (SectionObject->u.Flags.Based == 1)
3257     {
3258         /* Remove the node from the global section address tree */
3259         KeAcquireGuardedMutex(&MmSectionBasedMutex);
3260         MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot);
3261         KeReleaseGuardedMutex(&MmSectionBasedMutex);
3262     }
3263 
3264     /* Lock the PFN database */
3265     OldIrql = MiAcquirePfnLock();
3266 
3267     ASSERT(SectionObject->Segment);
3268     ASSERT(SectionObject->Segment->ControlArea);
3269 
3270     ControlArea = SectionObject->Segment->ControlArea;
3271 
3272     /* Dereference */
3273     ControlArea->NumberOfSectionReferences--;
3274     ControlArea->NumberOfUserReferences--;
3275 
3276     ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
3277 
3278     /* Check it. It will delete it if there is no more reference to it */
3279     MiCheckControlArea(ControlArea, OldIrql);
3280 }
3281 
3282 ULONG
3283 NTAPI
3284 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer)
3285 {
3286     UNIMPLEMENTED;
3287     return 0;
3288 }
3289 
3290 /* SYSTEM CALLS ***************************************************************/
3291 
3292 NTSTATUS
3293 NTAPI
3294 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3295                         IN PVOID File2MappedAsFile)
3296 {
3297     PVOID AddressSpace;
3298     PMMVAD Vad1, Vad2;
3299     PFILE_OBJECT FileObject1, FileObject2;
3300     NTSTATUS Status;
3301 
3302     /* Lock address space */
3303     AddressSpace = MmGetCurrentAddressSpace();
3304     MmLockAddressSpace(AddressSpace);
3305 
3306     /* Get the VAD for Address 1 */
3307     Vad1 = MiLocateAddress(File1MappedAsAnImage);
3308     if (Vad1 == NULL)
3309     {
3310         /* Fail, the address does not exist */
3311         DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage);
3312         Status = STATUS_INVALID_ADDRESS;
3313         goto Exit;
3314     }
3315 
3316     /* Get the VAD for Address 2 */
3317     Vad2 = MiLocateAddress(File2MappedAsFile);
3318     if (Vad2 == NULL)
3319     {
3320         /* Fail, the address does not exist */
3321         DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile);
3322         Status = STATUS_INVALID_ADDRESS;
3323         goto Exit;
3324     }
3325 
3326     /* Get the file object pointer for VAD 1 */
3327     FileObject1 = MiGetFileObjectForVad(Vad1);
3328     if (FileObject1 == NULL)
3329     {
3330         DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage);
3331         Status = STATUS_CONFLICTING_ADDRESSES;
3332         goto Exit;
3333     }
3334 
3335     /* Get the file object pointer for VAD 2 */
3336     FileObject2 = MiGetFileObjectForVad(Vad2);
3337     if (FileObject2 == NULL)
3338     {
3339         DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile);
3340         Status = STATUS_CONFLICTING_ADDRESSES;
3341         goto Exit;
3342     }
3343 
3344     /* Make sure Vad1 is an image mapping */
3345     if (Vad1->u.VadFlags.VadType != VadImageMap)
3346     {
3347         DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage);
3348         Status = STATUS_NOT_SAME_DEVICE;
3349         goto Exit;
3350     }
3351 
3352     /* SectionObjectPointer is equal if the files are equal */
3353     if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer)
3354     {
3355         Status = STATUS_SUCCESS;
3356     }
3357     else
3358     {
3359         Status = STATUS_NOT_SAME_DEVICE;
3360     }
3361 
3362 Exit:
3363     /* Unlock address space */
3364     MmUnlockAddressSpace(AddressSpace);
3365     return Status;
3366 }
3367 
3368 /*
3369  * @implemented
3370  */
3371 NTSTATUS
3372 NTAPI
3373 NtCreateSection(OUT PHANDLE SectionHandle,
3374                 IN ACCESS_MASK DesiredAccess,
3375                 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3376                 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3377                 IN ULONG SectionPageProtection OPTIONAL,
3378                 IN ULONG AllocationAttributes,
3379                 IN HANDLE FileHandle OPTIONAL)
3380 {
3381     LARGE_INTEGER SafeMaximumSize;
3382     PVOID SectionObject;
3383     HANDLE Handle;
3384     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3385     NTSTATUS Status;
3386     PAGED_CODE();
3387 
3388     /* Check for non-existing flags */
3389     if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3390                                   SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3391                                   SEC_NO_CHANGE)))
3392     {
3393         if (!(AllocationAttributes & 1))
3394         {
3395             DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3396             return STATUS_INVALID_PARAMETER_6;
3397         }
3398     }
3399 
3400     /* Check for no allocation type */
3401     if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3402     {
3403         DPRINT1("Missing allocation type in allocation attributes\n");
3404         return STATUS_INVALID_PARAMETER_6;
3405     }
3406 
3407     /* Check for image allocation with invalid attributes */
3408     if ((AllocationAttributes & SEC_IMAGE) &&
3409         (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3410                                  SEC_NOCACHE | SEC_NO_CHANGE)))
3411     {
3412         DPRINT1("Image allocation with invalid attributes\n");
3413         return STATUS_INVALID_PARAMETER_6;
3414     }
3415 
3416     /* Check for allocation type is both commit and reserve */
3417     if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3418     {
3419         DPRINT1("Commit and reserve in the same time\n");
3420         return STATUS_INVALID_PARAMETER_6;
3421     }
3422 
3423     /* Now check for valid protection */
3424     if ((SectionPageProtection & PAGE_NOCACHE) ||
3425         (SectionPageProtection & PAGE_WRITECOMBINE) ||
3426         (SectionPageProtection & PAGE_GUARD) ||
3427         (SectionPageProtection & PAGE_NOACCESS))
3428     {
3429         DPRINT1("Sections don't support these protections\n");
3430         return STATUS_INVALID_PAGE_PROTECTION;
3431     }
3432 
3433     /* Use a maximum size of zero, if none was specified */
3434     SafeMaximumSize.QuadPart = 0;
3435 
3436     /* Check for user-mode caller */
3437     if (PreviousMode != KernelMode)
3438     {
3439         /* Enter SEH */
3440         _SEH2_TRY
3441         {
3442             /* Safely check user-mode parameters */
3443             if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3444             MaximumSize = &SafeMaximumSize;
3445             ProbeForWriteHandle(SectionHandle);
3446         }
3447         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3448         {
3449             /* Return the exception code */
3450             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3451         }
3452         _SEH2_END;
3453     }
3454     else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
3455 
3456     /* Check that MaximumSize is valid if backed by paging file */
3457     if ((!FileHandle) && (!MaximumSize->QuadPart))
3458         return STATUS_INVALID_PARAMETER_4;
3459 
3460     /* Create the section */
3461     Status = MmCreateSection(&SectionObject,
3462                              DesiredAccess,
3463                              ObjectAttributes,
3464                              MaximumSize,
3465                              SectionPageProtection,
3466                              AllocationAttributes,
3467                              FileHandle,
3468                              NULL);
3469     if (!NT_SUCCESS(Status)) return Status;
3470 
3471     /* FIXME: Should zero last page for a file mapping */
3472 
3473     /* Now insert the object */
3474     Status = ObInsertObject(SectionObject,
3475                             NULL,
3476                             DesiredAccess,
3477                             0,
3478                             NULL,
3479                             &Handle);
3480     if (NT_SUCCESS(Status))
3481     {
3482         /* Enter SEH */
3483         _SEH2_TRY
3484         {
3485             /* Return the handle safely */
3486             *SectionHandle = Handle;
3487         }
3488         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3489         {
3490             /* Nothing here */
3491         }
3492         _SEH2_END;
3493     }
3494 
3495     /* Return the status */
3496     return Status;
3497 }
3498 
3499 NTSTATUS
3500 NTAPI
3501 NtOpenSection(OUT PHANDLE SectionHandle,
3502               IN ACCESS_MASK DesiredAccess,
3503               IN POBJECT_ATTRIBUTES ObjectAttributes)
3504 {
3505     HANDLE Handle;
3506     NTSTATUS Status;
3507     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3508     PAGED_CODE();
3509 
3510     /* Check for user-mode caller */
3511     if (PreviousMode != KernelMode)
3512     {
3513         /* Enter SEH */
3514         _SEH2_TRY
3515         {
3516             /* Safely check user-mode parameters */
3517             ProbeForWriteHandle(SectionHandle);
3518         }
3519         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3520         {
3521             /* Return the exception code */
3522             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3523         }
3524         _SEH2_END;
3525     }
3526 
3527     /* Try opening the object */
3528     Status = ObOpenObjectByName(ObjectAttributes,
3529                                 MmSectionObjectType,
3530                                 PreviousMode,
3531                                 NULL,
3532                                 DesiredAccess,
3533                                 NULL,
3534                                 &Handle);
3535 
3536     /* Enter SEH */
3537     _SEH2_TRY
3538     {
3539         /* Return the handle safely */
3540         *SectionHandle = Handle;
3541     }
3542     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3543     {
3544         /* Nothing here */
3545     }
3546     _SEH2_END;
3547 
3548     /* Return the status */
3549     return Status;
3550 }
3551 
3552 NTSTATUS
3553 NTAPI
3554 NtMapViewOfSection(IN HANDLE SectionHandle,
3555                    IN HANDLE ProcessHandle,
3556                    IN OUT PVOID* BaseAddress,
3557                    IN ULONG_PTR ZeroBits,
3558                    IN SIZE_T CommitSize,
3559                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3560                    IN OUT PSIZE_T ViewSize,
3561                    IN SECTION_INHERIT InheritDisposition,
3562                    IN ULONG AllocationType,
3563                    IN ULONG Protect)
3564 {
3565     PVOID SafeBaseAddress;
3566     LARGE_INTEGER SafeSectionOffset;
3567     SIZE_T SafeViewSize;
3568     PROS_SECTION_OBJECT Section;
3569     PEPROCESS Process;
3570     NTSTATUS Status;
3571     ACCESS_MASK DesiredAccess;
3572     ULONG ProtectionMask;
3573     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3574 #if defined(_M_IX86) || defined(_M_AMD64)
3575     static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES |
3576             MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE);
3577 #else
3578     static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES |
3579             SEC_NO_CHANGE | MEM_RESERVE);
3580 #endif
3581 
3582     /* Check for invalid inherit disposition */
3583     if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
3584     {
3585         DPRINT1("Invalid inherit disposition\n");
3586         return STATUS_INVALID_PARAMETER_8;
3587     }
3588 
3589     /* Allow only valid allocation types */
3590     if (AllocationType & ~ValidAllocationType)
3591     {
3592         DPRINT1("Invalid allocation type\n");
3593         return STATUS_INVALID_PARAMETER_9;
3594     }
3595 
3596     /* Convert the protection mask, and validate it */
3597     ProtectionMask = MiMakeProtectionMask(Protect);
3598     if (ProtectionMask == MM_INVALID_PROTECTION)
3599     {
3600         DPRINT1("Invalid page protection\n");
3601         return STATUS_INVALID_PAGE_PROTECTION;
3602     }
3603 
3604     /* Now convert the protection mask into desired section access mask */
3605     DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
3606 
3607     /* Assume no section offset */
3608     SafeSectionOffset.QuadPart = 0;
3609 
3610     /* Enter SEH */
3611     _SEH2_TRY
3612     {
3613         /* Check for unsafe parameters */
3614         if (PreviousMode != KernelMode)
3615         {
3616             /* Probe the parameters */
3617             ProbeForWritePointer(BaseAddress);
3618             ProbeForWriteSize_t(ViewSize);
3619         }
3620 
3621         /* Check if a section offset was given */
3622         if (SectionOffset)
3623         {
3624             /* Check for unsafe parameters and capture section offset */
3625             if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
3626             SafeSectionOffset = *SectionOffset;
3627         }
3628 
3629         /* Capture the other parameters */
3630         SafeBaseAddress = *BaseAddress;
3631         SafeViewSize = *ViewSize;
3632     }
3633     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3634     {
3635         /* Return the exception code */
3636         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3637     }
3638     _SEH2_END;
3639 
3640     /* Check for kernel-mode address */
3641     if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
3642     {
3643         DPRINT1("Kernel base not allowed\n");
3644         return STATUS_INVALID_PARAMETER_3;
3645     }
3646 
3647     /* Check for range entering kernel-mode */
3648     if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
3649     {
3650         DPRINT1("Overflowing into kernel base not allowed\n");
3651         return STATUS_INVALID_PARAMETER_3;
3652     }
3653 
3654     /* Check for invalid zero bits */
3655     if (ZeroBits)
3656     {
3657         if (ZeroBits > MI_MAX_ZERO_BITS)
3658         {
3659             DPRINT1("Invalid zero bits\n");
3660             return STATUS_INVALID_PARAMETER_4;
3661         }
3662 
3663         if ((((ULONG_PTR)SafeBaseAddress << ZeroBits) >> ZeroBits) != (ULONG_PTR)SafeBaseAddress)
3664         {
3665             DPRINT1("Invalid zero bits\n");
3666             return STATUS_INVALID_PARAMETER_4;
3667         }
3668 
3669         if (((((ULONG_PTR)SafeBaseAddress + SafeViewSize) << ZeroBits) >> ZeroBits) != ((ULONG_PTR)SafeBaseAddress + SafeViewSize))
3670         {
3671             DPRINT1("Invalid zero bits\n");
3672             return STATUS_INVALID_PARAMETER_4;
3673         }
3674     }
3675 
3676     /* Reference the process */
3677     Status = ObReferenceObjectByHandle(ProcessHandle,
3678                                        PROCESS_VM_OPERATION,
3679                                        PsProcessType,
3680                                        PreviousMode,
3681                                        (PVOID*)&Process,
3682                                        NULL);
3683     if (!NT_SUCCESS(Status)) return Status;
3684 
3685     /* Reference the section */
3686     Status = ObReferenceObjectByHandle(SectionHandle,
3687                                        DesiredAccess,
3688                                        MmSectionObjectType,
3689                                        PreviousMode,
3690                                        (PVOID*)&Section,
3691                                        NULL);
3692     if (!NT_SUCCESS(Status))
3693     {
3694         ObDereferenceObject(Process);
3695         return Status;
3696     }
3697 
3698     if (MiIsRosSectionObject(Section) &&
3699         (Section->AllocationAttributes & SEC_PHYSICALMEMORY))
3700     {
3701         if (PreviousMode == UserMode &&
3702             SafeSectionOffset.QuadPart + SafeViewSize > MmHighestPhysicalPage << PAGE_SHIFT)
3703         {
3704             DPRINT1("Denying map past highest physical page.\n");
3705             ObDereferenceObject(Section);
3706             ObDereferenceObject(Process);
3707             return STATUS_INVALID_PARAMETER_6;
3708         }
3709     }
3710     else if (!(AllocationType & MEM_DOS_LIM))
3711     {
3712         /* Check for non-allocation-granularity-aligned BaseAddress */
3713         if (SafeBaseAddress != ALIGN_DOWN_POINTER_BY(SafeBaseAddress, MM_VIRTMEM_GRANULARITY))
3714         {
3715             DPRINT("BaseAddress is not at 64-kilobyte address boundary.\n");
3716             ObDereferenceObject(Section);
3717             ObDereferenceObject(Process);
3718             return STATUS_MAPPED_ALIGNMENT;
3719         }
3720 
3721         /* Do the same for the section offset */
3722         if (SafeSectionOffset.LowPart != ALIGN_DOWN_BY(SafeSectionOffset.LowPart, MM_VIRTMEM_GRANULARITY))
3723         {
3724             DPRINT("SectionOffset is not at 64-kilobyte address boundary.\n");
3725             ObDereferenceObject(Section);
3726             ObDereferenceObject(Process);
3727             return STATUS_MAPPED_ALIGNMENT;
3728         }
3729     }
3730 
3731     /* Now do the actual mapping */
3732     Status = MmMapViewOfSection(Section,
3733                                 Process,
3734                                 &SafeBaseAddress,
3735                                 ZeroBits,
3736                                 CommitSize,
3737                                 &SafeSectionOffset,
3738                                 &SafeViewSize,
3739                                 InheritDisposition,
3740                                 AllocationType,
3741                                 Protect);
3742 
3743     /* Return data only on success */
3744     if (NT_SUCCESS(Status))
3745     {
3746         /* Check if this is an image for the current process */
3747         if (MiIsRosSectionObject(Section) &&
3748             (Section->AllocationAttributes & SEC_IMAGE) &&
3749             (Process == PsGetCurrentProcess()) &&
3750             (Status != STATUS_IMAGE_NOT_AT_BASE))
3751         {
3752             /* Notify the debugger */
3753             DbgkMapViewOfSection(Section,
3754                                  SafeBaseAddress,
3755                                  SafeSectionOffset.LowPart,
3756                                  SafeViewSize);
3757         }
3758 
3759         /* Enter SEH */
3760         _SEH2_TRY
3761         {
3762             /* Return parameters to user */
3763             *BaseAddress = SafeBaseAddress;
3764             *ViewSize = SafeViewSize;
3765             if (SectionOffset) *SectionOffset = SafeSectionOffset;
3766         }
3767         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3768         {
3769             /* Nothing to do */
3770         }
3771         _SEH2_END;
3772     }
3773 
3774     /* Dereference all objects and return status */
3775     ObDereferenceObject(Section);
3776     ObDereferenceObject(Process);
3777     return Status;
3778 }
3779 
3780 NTSTATUS
3781 NTAPI
3782 NtUnmapViewOfSection(IN HANDLE ProcessHandle,
3783                      IN PVOID BaseAddress)
3784 {
3785     PEPROCESS Process;
3786     NTSTATUS Status;
3787     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3788 
3789     /* Don't allowing mapping kernel views */
3790     if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
3791     {
3792         DPRINT1("Trying to unmap a kernel view\n");
3793         return STATUS_NOT_MAPPED_VIEW;
3794     }
3795 
3796     /* Reference the process */
3797     Status = ObReferenceObjectByHandle(ProcessHandle,
3798                                        PROCESS_VM_OPERATION,
3799                                        PsProcessType,
3800                                        PreviousMode,
3801                                        (PVOID*)&Process,
3802                                        NULL);
3803     if (!NT_SUCCESS(Status)) return Status;
3804 
3805     /* Unmap the view */
3806     Status = MiUnmapViewOfSection(Process, BaseAddress, 0);
3807 
3808     /* Dereference the process and return status */
3809     ObDereferenceObject(Process);
3810     return Status;
3811 }
3812 
3813 NTSTATUS
3814 NTAPI
3815 NtExtendSection(IN HANDLE SectionHandle,
3816                 IN OUT PLARGE_INTEGER NewMaximumSize)
3817 {
3818     LARGE_INTEGER SafeNewMaximumSize;
3819     PROS_SECTION_OBJECT Section;
3820     NTSTATUS Status;
3821     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3822 
3823     /* Check for user-mode parameters */
3824     if (PreviousMode != KernelMode)
3825     {
3826         /* Enter SEH */
3827         _SEH2_TRY
3828         {
3829             /* Probe and capture the maximum size, it's both read and write */
3830             ProbeForWriteLargeInteger(NewMaximumSize);
3831             SafeNewMaximumSize = *NewMaximumSize;
3832         }
3833         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3834         {
3835             /* Return the exception code */
3836             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3837         }
3838         _SEH2_END;
3839     }
3840     else
3841     {
3842         /* Just read the size directly */
3843         SafeNewMaximumSize = *NewMaximumSize;
3844     }
3845 
3846     /* Reference the section */
3847     Status = ObReferenceObjectByHandle(SectionHandle,
3848                                        SECTION_EXTEND_SIZE,
3849                                        MmSectionObjectType,
3850                                        PreviousMode,
3851                                        (PVOID*)&Section,
3852                                        NULL);
3853     if (!NT_SUCCESS(Status)) return Status;
3854 
3855     /* Really this should go in MmExtendSection */
3856     if (!(Section->AllocationAttributes & SEC_FILE))
3857     {
3858         DPRINT1("Not extending a file\n");
3859         ObDereferenceObject(Section);
3860         return STATUS_SECTION_NOT_EXTENDED;
3861     }
3862 
3863     /* FIXME: Do the work */
3864 
3865     /* Dereference the section */
3866     ObDereferenceObject(Section);
3867 
3868     /* Enter SEH */
3869     _SEH2_TRY
3870     {
3871         /* Write back the new size */
3872         *NewMaximumSize = SafeNewMaximumSize;
3873     }
3874     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3875     {
3876         /* Nothing to do */
3877     }
3878     _SEH2_END;
3879 
3880     /* Return the status */
3881     return STATUS_NOT_IMPLEMENTED;
3882 }
3883 
3884 /* EOF */
3885