xref: /reactos/ntoskrnl/mm/ARM3/section.c (revision 9592728f)
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_COUNT];
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 (!((ULONG_PTR)PointerPte & (PD_SIZE - 1)) &&
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                 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1927                 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1928                 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1929 
1930                 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1931 
1932             }
1933             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1934             {
1935                 Status = _SEH2_GetExceptionCode();
1936             }
1937             _SEH2_END;
1938         }
1939         else
1940         {
1941             RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1942             SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1943             RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1944 
1945             if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1946 
1947         }
1948 
1949         RtlFreeUnicodeString(&ModuleFileName);
1950     }
1951     ObDereferenceObject(Process);
1952     return Status;
1953 }
1954 
1955 VOID
1956 NTAPI
1957 MiFlushTbAndCapture(IN PMMVAD FoundVad,
1958                     IN PMMPTE PointerPte,
1959                     IN ULONG ProtectionMask,
1960                     IN PMMPFN Pfn1,
1961                     IN BOOLEAN UpdateDirty)
1962 {
1963     MMPTE TempPte, PreviousPte;
1964     KIRQL OldIrql;
1965     BOOLEAN RebuildPte = FALSE;
1966 
1967     //
1968     // User for sanity checking later on
1969     //
1970     PreviousPte = *PointerPte;
1971 
1972     //
1973     // Build the PTE and acquire the PFN lock
1974     //
1975     MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1976                               PointerPte,
1977                               ProtectionMask,
1978                               PreviousPte.u.Hard.PageFrameNumber);
1979     OldIrql = MiAcquirePfnLock();
1980 
1981     //
1982     // We don't support I/O mappings in this path yet
1983     //
1984     ASSERT(Pfn1 != NULL);
1985     ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined);
1986 
1987     //
1988     // Make sure new protection mask doesn't get in conflict and fix it if it does
1989     //
1990     if (Pfn1->u3.e1.CacheAttribute == MiCached)
1991     {
1992         //
1993         // This is a cached PFN
1994         //
1995         if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS))
1996         {
1997             RebuildPte = TRUE;
1998             ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS);
1999         }
2000     }
2001     else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
2002     {
2003         //
2004         // This is a non-cached PFN
2005         //
2006         if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE)
2007         {
2008             RebuildPte = TRUE;
2009             ProtectionMask &= ~MM_NOACCESS;
2010             ProtectionMask |= MM_NOCACHE;
2011         }
2012     }
2013 
2014     if (RebuildPte)
2015     {
2016         MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2017                                   PointerPte,
2018                                   ProtectionMask,
2019                                   PreviousPte.u.Hard.PageFrameNumber);
2020     }
2021 
2022     //
2023     // Write the new PTE, making sure we are only changing the bits
2024     //
2025     MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2026 
2027     //
2028     // Flush the TLB
2029     //
2030     ASSERT(PreviousPte.u.Hard.Valid == 1);
2031     KeFlushCurrentTb();
2032     ASSERT(PreviousPte.u.Hard.Valid == 1);
2033 
2034     //
2035     // Windows updates the relevant PFN1 information, we currently don't.
2036     //
2037     if (UpdateDirty && PreviousPte.u.Hard.Dirty)
2038     {
2039         if (!Pfn1->u3.e1.Modified)
2040         {
2041             DPRINT1("FIXME: Mark PFN as dirty\n");
2042         }
2043     }
2044 
2045     //
2046     // Not supported in ARM3
2047     //
2048     ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
2049 
2050     //
2051     // Release the PFN lock, we are done
2052     //
2053     MiReleasePfnLock(OldIrql);
2054 }
2055 
2056 //
2057 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
2058 //
2059 NTSTATUS
2060 NTAPI
2061 MiSetProtectionOnSection(IN PEPROCESS Process,
2062                          IN PMMVAD FoundVad,
2063                          IN PVOID StartingAddress,
2064                          IN PVOID EndingAddress,
2065                          IN ULONG NewProtect,
2066                          OUT PULONG CapturedOldProtect,
2067                          IN ULONG DontCharge,
2068                          OUT PULONG Locked)
2069 {
2070     PMMPTE PointerPte, LastPte;
2071     MMPTE TempPte, PteContents;
2072     PMMPDE PointerPde;
2073     PMMPFN Pfn1;
2074     ULONG ProtectionMask, QuotaCharge = 0;
2075     PETHREAD Thread = PsGetCurrentThread();
2076     PAGED_CODE();
2077 
2078     //
2079     // Tell caller nothing is being locked
2080     //
2081     *Locked = FALSE;
2082 
2083     //
2084     // This function should only be used for section VADs. Windows ASSERT */
2085     //
2086     ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
2087 
2088     //
2089     // We don't support these features in ARM3
2090     //
2091     ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2092     ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
2093 
2094     //
2095     // Convert and validate the protection mask
2096     //
2097     ProtectionMask = MiMakeProtectionMask(NewProtect);
2098     if (ProtectionMask == MM_INVALID_PROTECTION)
2099     {
2100         DPRINT1("Invalid section protect\n");
2101         return STATUS_INVALID_PAGE_PROTECTION;
2102     }
2103 
2104     //
2105     // Get the PTE and PDE for the address, as well as the final PTE
2106     //
2107     MiLockProcessWorkingSetUnsafe(Process, Thread);
2108     PointerPde = MiAddressToPde(StartingAddress);
2109     PointerPte = MiAddressToPte(StartingAddress);
2110     LastPte = MiAddressToPte(EndingAddress);
2111 
2112     //
2113     // Make the PDE valid, and check the status of the first PTE
2114     //
2115     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2116     if (PointerPte->u.Long)
2117     {
2118         //
2119         // Not supported in ARM3
2120         //
2121         ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2122 
2123         //
2124         // Capture the page protection and make the PDE valid
2125         //
2126         *CapturedOldProtect = MiGetPageProtection(PointerPte);
2127         MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2128     }
2129     else
2130     {
2131         //
2132         // Only pagefile-backed section VADs are supported for now
2133         //
2134         ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2135 
2136         //
2137         // Grab the old protection from the VAD itself
2138         //
2139         *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2140     }
2141 
2142     //
2143     // Loop all the PTEs now
2144     //
2145     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2146     while (PointerPte <= LastPte)
2147     {
2148         //
2149         // Check if we've crossed a PDE boundary and make the new PDE valid too
2150         //
2151         if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
2152         {
2153             PointerPde = MiPteToPde(PointerPte);
2154             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2155         }
2156 
2157         //
2158         // Capture the PTE and see what we're dealing with
2159         //
2160         PteContents = *PointerPte;
2161         if (PteContents.u.Long == 0)
2162         {
2163             //
2164             // This used to be a zero PTE and it no longer is, so we must add a
2165             // reference to the pagetable.
2166             //
2167             MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2168 
2169             //
2170             // Create the demand-zero prototype PTE
2171             //
2172             TempPte = PrototypePte;
2173             TempPte.u.Soft.Protection = ProtectionMask;
2174             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2175         }
2176         else if (PteContents.u.Hard.Valid == 1)
2177         {
2178             //
2179             // Get the PFN entry
2180             //
2181             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2182 
2183             //
2184             // We don't support these yet
2185             //
2186             ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2187             ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2188 
2189             //
2190             // Write the protection mask and write it with a TLB flush
2191             //
2192             Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2193             MiFlushTbAndCapture(FoundVad,
2194                                 PointerPte,
2195                                 ProtectionMask,
2196                                 Pfn1,
2197                                 TRUE);
2198         }
2199         else
2200         {
2201             //
2202             // We don't support these cases yet
2203             //
2204             ASSERT(PteContents.u.Soft.Prototype == 0);
2205             ASSERT(PteContents.u.Soft.Transition == 0);
2206 
2207             //
2208             // The PTE is already demand-zero, just update the protection mask
2209             //
2210             PointerPte->u.Soft.Protection = ProtectionMask;
2211         }
2212 
2213         PointerPte++;
2214     }
2215 
2216     //
2217     // Unlock the working set and update quota charges if needed, then return
2218     //
2219     MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2220     if ((QuotaCharge > 0) && (!DontCharge))
2221     {
2222         FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2223         Process->CommitCharge -= QuotaCharge;
2224     }
2225     return STATUS_SUCCESS;
2226 }
2227 
2228 VOID
2229 NTAPI
2230 MiRemoveMappedPtes(IN PVOID BaseAddress,
2231                    IN ULONG NumberOfPtes,
2232                    IN PCONTROL_AREA ControlArea,
2233                    IN PMMSUPPORT Ws)
2234 {
2235     PMMPTE PointerPte, ProtoPte;//, FirstPte;
2236     PMMPDE PointerPde, SystemMapPde;
2237     PMMPFN Pfn1, Pfn2;
2238     MMPTE PteContents;
2239     KIRQL OldIrql;
2240     DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2241 
2242     ASSERT(Ws == NULL);
2243 
2244     /* Get the PTE and loop each one */
2245     PointerPte = MiAddressToPte(BaseAddress);
2246     //FirstPte = PointerPte;
2247     while (NumberOfPtes)
2248     {
2249         /* Check if the PTE is already valid */
2250         PteContents = *PointerPte;
2251         if (PteContents.u.Hard.Valid == 1)
2252         {
2253             /* Get the PFN entry */
2254             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2255 
2256             /* Get the PTE */
2257             PointerPde = MiPteToPde(PointerPte);
2258 
2259             /* Lock the PFN database and make sure this isn't a mapped file */
2260             OldIrql = MiAcquirePfnLock();
2261             ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2262 
2263             /* Mark the page as modified accordingly */
2264             if (MI_IS_PAGE_DIRTY(&PteContents))
2265                 Pfn1->u3.e1.Modified = 1;
2266 
2267             /* Was the PDE invalid */
2268             if (PointerPde->u.Long == 0)
2269             {
2270 #if (_MI_PAGING_LEVELS == 2)
2271                 /* Find the system double-mapped PDE that describes this mapping */
2272                 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2273 
2274                 /* Make it valid */
2275                 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2276                 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2277 #else
2278                 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde);
2279                 ASSERT(FALSE);
2280 #endif
2281             }
2282 
2283             /* Dereference the PDE and the PTE */
2284             Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2285             MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2286             DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2287             MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2288 
2289             /* Release the PFN lock */
2290             MiReleasePfnLock(OldIrql);
2291         }
2292         else
2293         {
2294             /* Windows ASSERT */
2295             ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2296 
2297             /* Check if this is a prototype pointer PTE */
2298             if (PteContents.u.Soft.Prototype == 1)
2299             {
2300                 /* Get the prototype PTE */
2301                 ProtoPte = MiProtoPteToPte(&PteContents);
2302 
2303                 /* We don't support anything else atm */
2304                 ASSERT(ProtoPte->u.Long == 0);
2305             }
2306         }
2307 
2308         /* Make the PTE into a zero PTE */
2309         PointerPte->u.Long = 0;
2310 
2311         /* Move to the next PTE */
2312         PointerPte++;
2313         NumberOfPtes--;
2314     }
2315 
2316     /* Flush the TLB */
2317     KeFlushCurrentTb();
2318 
2319     /* Acquire the PFN lock */
2320     OldIrql = MiAcquirePfnLock();
2321 
2322     /* Decrement the accounting counters */
2323     ControlArea->NumberOfUserReferences--;
2324     ControlArea->NumberOfMappedViews--;
2325 
2326     /* Check if we should destroy the CA and release the lock */
2327     MiCheckControlArea(ControlArea, OldIrql);
2328 }
2329 
2330 ULONG
2331 NTAPI
2332 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2333                         IN PVOID Base,
2334                         OUT PCONTROL_AREA *ControlArea)
2335 {
2336     ULONG Hash, Size, Count = 0;
2337     ULONG_PTR Entry;
2338     PAGED_CODE();
2339 
2340     /* Compute the hash for this entry and loop trying to find it */
2341     Entry = (ULONG_PTR)Base >> 16;
2342     Hash = Entry % Session->SystemSpaceHashKey;
2343     while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2344     {
2345         /* Check if we overflew past the end of the hash table */
2346         if (++Hash >= Session->SystemSpaceHashSize)
2347         {
2348             /* Reset the hash to zero and keep searching from the bottom */
2349             Hash = 0;
2350             if (++Count == 2)
2351             {
2352                 /* But if we overflew twice, then this is not a real mapping */
2353                 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2354                              (ULONG_PTR)Base,
2355                              1,
2356                              0,
2357                              0);
2358             }
2359         }
2360     }
2361 
2362     /* One less entry */
2363     Session->SystemSpaceHashEntries--;
2364 
2365     /* Extract the size and clear the entry */
2366     Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2367     Session->SystemSpaceViewTable[Hash].Entry = 0;
2368 
2369     /* Return the control area and the size */
2370     *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2371     return Size;
2372 }
2373 
2374 NTSTATUS
2375 NTAPI
2376 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2377                          IN PVOID MappedBase)
2378 {
2379     ULONG Size;
2380     PCONTROL_AREA ControlArea;
2381     PAGED_CODE();
2382 
2383     /* Remove this mapping */
2384     KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2385     Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2386 
2387     /* Clear the bits for this mapping */
2388     RtlClearBits(Session->SystemSpaceBitMap,
2389                  (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2390                  Size);
2391 
2392     /* Convert the size from a bit size into the actual size */
2393     Size = Size * (_64K >> PAGE_SHIFT);
2394 
2395     /* Remove the PTEs now */
2396     MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2397     KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2398 
2399     /* Return success */
2400     return STATUS_SUCCESS;
2401 }
2402 
2403 /* PUBLIC FUNCTIONS ***********************************************************/
2404 
2405 /*
2406  * @implemented
2407  */
2408 NTSTATUS
2409 NTAPI
2410 MmCreateArm3Section(OUT PVOID *SectionObject,
2411                     IN ACCESS_MASK DesiredAccess,
2412                     IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2413                     IN PLARGE_INTEGER InputMaximumSize,
2414                     IN ULONG SectionPageProtection,
2415                     IN ULONG AllocationAttributes,
2416                     IN HANDLE FileHandle OPTIONAL,
2417                     IN PFILE_OBJECT FileObject OPTIONAL)
2418 {
2419     SECTION Section;
2420     PSECTION NewSection;
2421     PSUBSECTION Subsection;
2422     PSEGMENT NewSegment, Segment;
2423     NTSTATUS Status;
2424     PCONTROL_AREA ControlArea;
2425     ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2426     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2427     BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2428     KIRQL OldIrql;
2429     PFILE_OBJECT File;
2430     BOOLEAN UserRefIncremented = FALSE;
2431     PVOID PreviousSectionPointer;
2432 
2433     /* Make the same sanity checks that the Nt interface should've validated */
2434     ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2435                                      SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2436                                      SEC_NO_CHANGE)) == 0);
2437     ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2438     ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2439              (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2440                                       SEC_NOCACHE | SEC_NO_CHANGE))));
2441     ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2442     ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2443              (SectionPageProtection & PAGE_WRITECOMBINE) ||
2444              (SectionPageProtection & PAGE_GUARD) ||
2445              (SectionPageProtection & PAGE_NOACCESS)));
2446 
2447     /* Convert section flag to page flag */
2448     if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2449 
2450     /* Check to make sure the protection is correct. Nt* does this already */
2451     ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2452     if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2453 
2454     /* Check if this is going to be a data or image backed file section */
2455     if ((FileHandle) || (FileObject))
2456     {
2457         /* These cannot be mapped with large pages */
2458         if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2459 
2460         /* For now, only support the mechanism through a file handle */
2461         ASSERT(FileObject == NULL);
2462 
2463         /* Reference the file handle to get the object */
2464         Status = ObReferenceObjectByHandle(FileHandle,
2465                                            MmMakeFileAccess[ProtectionMask],
2466                                            IoFileObjectType,
2467                                            PreviousMode,
2468                                            (PVOID*)&File,
2469                                            NULL);
2470         if (!NT_SUCCESS(Status)) return Status;
2471 
2472         /* Make sure Cc has been doing its job */
2473         if (!File->SectionObjectPointer)
2474         {
2475             /* This is not a valid file system-based file, fail */
2476             ObDereferenceObject(File);
2477             return STATUS_INVALID_FILE_FOR_SECTION;
2478         }
2479 
2480         /* Image-file backed sections are not yet supported */
2481         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2482 
2483         /* Compute the size of the control area, and allocate it */
2484         ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2485         ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2486         if (!ControlArea)
2487         {
2488             ObDereferenceObject(File);
2489             return STATUS_INSUFFICIENT_RESOURCES;
2490         }
2491 
2492         /* Zero it out */
2493         RtlZeroMemory(ControlArea, ControlAreaSize);
2494 
2495         /* Did we get a handle, or an object? */
2496         if (FileHandle)
2497         {
2498             /* We got a file handle so we have to lock down the file */
2499 #if 0
2500             Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2501             if (!NT_SUCCESS(Status))
2502             {
2503                 ExFreePool(ControlArea);
2504                 ObDereferenceObject(File);
2505                 return Status;
2506             }
2507 #else
2508             /* ReactOS doesn't support this API yet, so do nothing */
2509             Status = STATUS_SUCCESS;
2510 #endif
2511             /* Update the top-level IRP so that drivers know what's happening */
2512             IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2513             FileLock = TRUE;
2514         }
2515 
2516         /* Lock the PFN database while we play with the section pointers */
2517         OldIrql = MiAcquirePfnLock();
2518 
2519         /* Image-file backed sections are not yet supported */
2520         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2521 
2522         /* There should not already be a control area for this file */
2523         ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2524         NewSegment = NULL;
2525 
2526         /* Write down that this CA is being created, and set it */
2527         ControlArea->u.Flags.BeingCreated = TRUE;
2528         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2529         PreviousSectionPointer = File->SectionObjectPointer;
2530         File->SectionObjectPointer->DataSectionObject = ControlArea;
2531 
2532         /* We can release the PFN lock now */
2533         MiReleasePfnLock(OldIrql);
2534 
2535         /* We don't support previously-mapped file */
2536         ASSERT(NewSegment == NULL);
2537 
2538         /* Image-file backed sections are not yet supported */
2539         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2540 
2541         /* So we always create a data file map */
2542         Status = MiCreateDataFileMap(File,
2543                                      &Segment,
2544                                      (PSIZE_T)InputMaximumSize,
2545                                      SectionPageProtection,
2546                                      AllocationAttributes,
2547                                      KernelCall);
2548         if (!NT_SUCCESS(Status))
2549         {
2550             /* Lock the PFN database while we play with the section pointers */
2551             OldIrql = MiAcquirePfnLock();
2552 
2553             /* Reset the waiting-for-deletion event */
2554             ASSERT(ControlArea->WaitingForDeletion == NULL);
2555             ControlArea->WaitingForDeletion = NULL;
2556 
2557             /* Set the file pointer NULL flag */
2558             ASSERT(ControlArea->u.Flags.FilePointerNull == 0);
2559             ControlArea->u.Flags.FilePointerNull = TRUE;
2560 
2561             /* Delete the data section object */
2562             ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2563             File->SectionObjectPointer->DataSectionObject = NULL;
2564 
2565             /* No longer being created */
2566             ControlArea->u.Flags.BeingCreated = FALSE;
2567 
2568             /* We can release the PFN lock now */
2569             MiReleasePfnLock(OldIrql);
2570 
2571             /* Check if we locked and set the IRP */
2572             if (FileLock)
2573             {
2574                 /* Undo */
2575                 IoSetTopLevelIrp(NULL);
2576                 //FsRtlReleaseFile(File);
2577             }
2578 
2579             /* Free the control area and de-ref the file object */
2580             ExFreePool(ControlArea);
2581             ObDereferenceObject(File);
2582 
2583             /* All done */
2584             return Status;
2585         }
2586 
2587         /* On success, we expect this */
2588         ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2589 
2590         /* Check if a maximum size was specified */
2591         if (!InputMaximumSize->QuadPart)
2592         {
2593             /* Nope, use the segment size */
2594             Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2595         }
2596         else
2597         {
2598             /* Yep, use the entered size */
2599             Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2600         }
2601     }
2602     else
2603     {
2604         /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2605         if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2606 
2607         /* Not yet supported */
2608         ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2609 
2610         /* So this must be a pagefile-backed section, create the mappings needed */
2611         Status = MiCreatePagingFileMap(&NewSegment,
2612                                        (PSIZE_T)InputMaximumSize,
2613                                        ProtectionMask,
2614                                        AllocationAttributes);
2615         if (!NT_SUCCESS(Status)) return Status;
2616 
2617         /* Set the size here, and read the control area */
2618         Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2619         ControlArea = NewSegment->ControlArea;
2620 
2621         /* MiCreatePagingFileMap increments user references */
2622         UserRefIncremented = TRUE;
2623     }
2624 
2625     /* Did we already have a segment? */
2626     if (!NewSegment)
2627     {
2628         /* This must be the file path and we created a segment */
2629         NewSegment = Segment;
2630         ASSERT(File != NULL);
2631 
2632         /* Acquire the PFN lock while we set control area flags */
2633         OldIrql = MiAcquirePfnLock();
2634 
2635         /* We don't support this race condition yet, so assume no waiters */
2636         ASSERT(ControlArea->WaitingForDeletion == NULL);
2637         ControlArea->WaitingForDeletion = NULL;
2638 
2639         /* Image-file backed sections are not yet supported, nor ROM images */
2640         ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2641         ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2642 
2643         /* Take off the being created flag, and then release the lock */
2644         ControlArea->u.Flags.BeingCreated = FALSE;
2645         MiReleasePfnLock(OldIrql);
2646     }
2647 
2648     /* Check if we locked the file earlier */
2649     if (FileLock)
2650     {
2651         /* Reset the top-level IRP and release the lock */
2652         IoSetTopLevelIrp(NULL);
2653         //FsRtlReleaseFile(File);
2654         FileLock = FALSE;
2655     }
2656 
2657     /* Set the initial section object data */
2658     Section.InitialPageProtection = SectionPageProtection;
2659 
2660     /* The mapping created a control area and segment, save the flags */
2661     Section.Segment = NewSegment;
2662     Section.u.LongFlags = ControlArea->u.LongFlags;
2663 
2664     /* Check if this is a user-mode read-write non-image file mapping */
2665     if (!(FileObject) &&
2666         (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2667         !(ControlArea->u.Flags.Image) &&
2668         (ControlArea->FilePointer))
2669     {
2670         /* Add a reference and set the flag */
2671         Section.u.Flags.UserWritable = TRUE;
2672         InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences);
2673     }
2674 
2675     /* Check for image mappings or page file mappings */
2676     if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer))
2677     {
2678         /* Charge the segment size, and allocate a subsection */
2679         PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2680         Size = sizeof(SUBSECTION);
2681     }
2682     else
2683     {
2684         /* Charge nothing, and allocate a mapped subsection */
2685         PagedCharge = 0;
2686         Size = sizeof(MSUBSECTION);
2687     }
2688 
2689     /* Check if this is a normal CA */
2690     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2691     ASSERT(ControlArea->u.Flags.Rom == 0);
2692 
2693     /* Charge only a CA, and the subsection is right after */
2694     NonPagedCharge = sizeof(CONTROL_AREA);
2695     Subsection = (PSUBSECTION)(ControlArea + 1);
2696 
2697     /* We only support single-subsection mappings */
2698     NonPagedCharge += Size;
2699     ASSERT(Subsection->NextSubsection == NULL);
2700 
2701     /* Create the actual section object, with enough space for the prototype PTEs */
2702     Status = ObCreateObject(PreviousMode,
2703                             MmSectionObjectType,
2704                             ObjectAttributes,
2705                             PreviousMode,
2706                             NULL,
2707                             sizeof(SECTION),
2708                             PagedCharge,
2709                             NonPagedCharge,
2710                             (PVOID*)&NewSection);
2711     if (!NT_SUCCESS(Status))
2712     {
2713         /* Check if this is a user-mode read-write non-image file mapping */
2714         if (!(FileObject) &&
2715             (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2716             !(ControlArea->u.Flags.Image) &&
2717             (ControlArea->FilePointer))
2718         {
2719             /* Remove a reference and check the flag */
2720             ASSERT(Section.u.Flags.UserWritable == 1);
2721             InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences);
2722         }
2723 
2724         /* Check if a user reference was added */
2725         if (UserRefIncremented)
2726         {
2727             /* Acquire the PFN lock while we change counters */
2728             OldIrql = MiAcquirePfnLock();
2729 
2730             /* Decrement the accounting counters */
2731             ControlArea->NumberOfSectionReferences--;
2732             ASSERT((LONG)ControlArea->NumberOfUserReferences > 0);
2733             ControlArea->NumberOfUserReferences--;
2734 
2735             /* Check if we should destroy the CA and release the lock */
2736             MiCheckControlArea(ControlArea, OldIrql);
2737         }
2738 
2739         /* Return the failure code */
2740         return Status;
2741     }
2742 
2743     /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */
2744 
2745     /* Now copy the local section object from the stack into this new object */
2746     RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2747     NewSection->Address.StartingVpn = 0;
2748 
2749     /* For now, only user calls are supported */
2750     ASSERT(KernelCall == FALSE);
2751     NewSection->u.Flags.UserReference = TRUE;
2752 
2753     /* Is this a "based" allocation, in which all mappings are identical? */
2754     if (AllocationAttributes & SEC_BASED)
2755     {
2756         /* Lock the VAD tree during the search */
2757         KeAcquireGuardedMutex(&MmSectionBasedMutex);
2758 
2759         /* Is it a brand new ControArea ? */
2760         if (ControlArea->u.Flags.BeingCreated == 1)
2761         {
2762             ASSERT(ControlArea->u.Flags.Based == 1);
2763             /* Then we must find a global address, top-down */
2764             Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment,
2765                                                           (ULONG_PTR)MmHighSectionBase,
2766                                                           _64K,
2767                                                           &MmSectionBasedRoot,
2768                                                           (ULONG_PTR*)&ControlArea->Segment->BasedAddress);
2769 
2770             if (!NT_SUCCESS(Status))
2771             {
2772                 /* No way to find a valid range. */
2773                 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2774                 ControlArea->u.Flags.Based = 0;
2775                 NewSection->u.Flags.Based = 0;
2776                 ObDereferenceObject(NewSection);
2777                 return Status;
2778             }
2779 
2780             /* Compute the ending address and insert it into the VAD tree */
2781             NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress;
2782             NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1;
2783             MiInsertBasedSection(NewSection);
2784         }
2785         else
2786         {
2787             /* 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 !*/
2788             ASSERT(FALSE);
2789         }
2790 
2791         KeReleaseGuardedMutex(&MmSectionBasedMutex);
2792     }
2793 
2794     /* The control area is not being created anymore */
2795     if (ControlArea->u.Flags.BeingCreated == 1)
2796     {
2797         /* Acquire the PFN lock while we set control area flags */
2798         OldIrql = MiAcquirePfnLock();
2799 
2800         /* Take off the being created flag, and then release the lock */
2801         ControlArea->u.Flags.BeingCreated = 0;
2802         NewSection->u.Flags.BeingCreated = 0;
2803 
2804         MiReleasePfnLock(OldIrql);
2805     }
2806 
2807     /* Migrate the attribute into a flag */
2808     if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2809 
2810     /* If R/W access is not requested, this might eventually become a CoW mapping */
2811     if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2812     {
2813         NewSection->u.Flags.CopyOnWrite = TRUE;
2814     }
2815 
2816     /* Write down if this was a kernel call */
2817     ControlArea->u.Flags.WasPurged |= KernelCall;
2818     ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2819 
2820     /* Make sure the segment and the section are the same size, or the section is smaller */
2821     ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2822 
2823     /* Return the object and the creation status */
2824     *SectionObject = (PVOID)NewSection;
2825     return Status;
2826 }
2827 
2828 /*
2829  * @implemented
2830  */
2831 NTSTATUS
2832 NTAPI
2833 MmMapViewOfArm3Section(IN PVOID SectionObject,
2834                        IN PEPROCESS Process,
2835                        IN OUT PVOID *BaseAddress,
2836                        IN ULONG_PTR ZeroBits,
2837                        IN SIZE_T CommitSize,
2838                        IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2839                        IN OUT PSIZE_T ViewSize,
2840                        IN SECTION_INHERIT InheritDisposition,
2841                        IN ULONG AllocationType,
2842                        IN ULONG Protect)
2843 {
2844     KAPC_STATE ApcState;
2845     BOOLEAN Attached = FALSE;
2846     PSECTION Section;
2847     PCONTROL_AREA ControlArea;
2848     ULONG ProtectionMask;
2849     NTSTATUS Status;
2850     ULONG64 CalculatedViewSize;
2851     PAGED_CODE();
2852 
2853     /* Get the segment and control area */
2854     Section = (PSECTION)SectionObject;
2855     ControlArea = Section->Segment->ControlArea;
2856 
2857     /* These flags/states are not yet supported by ARM3 */
2858     ASSERT(Section->u.Flags.Image == 0);
2859     ASSERT(Section->u.Flags.NoCache == 0);
2860     ASSERT(Section->u.Flags.WriteCombined == 0);
2861     ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2862 
2863     /* FIXME */
2864     if ((AllocationType & MEM_RESERVE) != 0)
2865     {
2866         DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2867         return STATUS_NOT_IMPLEMENTED;
2868     }
2869 
2870     /* Check if the mapping protection is compatible with the create */
2871     if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2872     {
2873         DPRINT1("Mapping protection is incompatible\n");
2874         return STATUS_SECTION_PROTECTION;
2875     }
2876 
2877     /* Check if the offset and size would cause an overflow */
2878     if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2879          (ULONG64)SectionOffset->QuadPart)
2880     {
2881         DPRINT1("Section offset overflows\n");
2882         return STATUS_INVALID_VIEW_SIZE;
2883     }
2884 
2885     /* Check if the offset and size are bigger than the section itself */
2886     if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2887          (ULONG64)Section->SizeOfSection.QuadPart)
2888     {
2889         DPRINT1("Section offset is larger than section\n");
2890         return STATUS_INVALID_VIEW_SIZE;
2891     }
2892 
2893     /* Check if the caller did not specify a view size */
2894     if (!(*ViewSize))
2895     {
2896         /* Compute it for the caller */
2897         CalculatedViewSize = Section->SizeOfSection.QuadPart -
2898                              SectionOffset->QuadPart;
2899 
2900         /* Check if it's larger than 4GB or overflows into kernel-mode */
2901         if (!NT_SUCCESS(RtlULongLongToSIZET(CalculatedViewSize, ViewSize)) ||
2902             (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < CalculatedViewSize))
2903         {
2904             DPRINT1("Section view won't fit\n");
2905             return STATUS_INVALID_VIEW_SIZE;
2906         }
2907     }
2908 
2909     /* Check if the commit size is larger than the view size */
2910     if (CommitSize > *ViewSize)
2911     {
2912         DPRINT1("Attempting to commit more than the view itself\n");
2913         return STATUS_INVALID_PARAMETER_5;
2914     }
2915 
2916     /* Check if the view size is larger than the section */
2917     if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2918     {
2919         DPRINT1("The view is larger than the section\n");
2920         return STATUS_INVALID_VIEW_SIZE;
2921     }
2922 
2923     /* Compute and validate the protection mask */
2924     ProtectionMask = MiMakeProtectionMask(Protect);
2925     if (ProtectionMask == MM_INVALID_PROTECTION)
2926     {
2927         DPRINT1("The protection is invalid\n");
2928         return STATUS_INVALID_PAGE_PROTECTION;
2929     }
2930 
2931     /* We only handle pagefile-backed sections, which cannot be writecombined */
2932     if (Protect & PAGE_WRITECOMBINE)
2933     {
2934         DPRINT1("Cannot write combine a pagefile-backed section\n");
2935         return STATUS_INVALID_PARAMETER_10;
2936     }
2937 
2938     /* Start by attaching to the current process if needed */
2939     if (PsGetCurrentProcess() != Process)
2940     {
2941         KeStackAttachProcess(&Process->Pcb, &ApcState);
2942         Attached = TRUE;
2943     }
2944 
2945     /* Do the actual mapping */
2946     Status = MiMapViewOfDataSection(ControlArea,
2947                                     Process,
2948                                     BaseAddress,
2949                                     SectionOffset,
2950                                     ViewSize,
2951                                     Section,
2952                                     InheritDisposition,
2953                                     ProtectionMask,
2954                                     CommitSize,
2955                                     ZeroBits,
2956                                     AllocationType);
2957 
2958     /* Detach if needed, then return status */
2959     if (Attached) KeUnstackDetachProcess(&ApcState);
2960     return Status;
2961 }
2962 
2963 /*
2964  * @unimplemented
2965  */
2966 BOOLEAN
2967 NTAPI
2968 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2969 {
2970    UNIMPLEMENTED;
2971    return FALSE;
2972 }
2973 
2974 /*
2975  * @unimplemented
2976  */
2977 BOOLEAN
2978 NTAPI
2979 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2980                      IN BOOLEAN DelayClose)
2981 {
2982    UNIMPLEMENTED;
2983    return FALSE;
2984 }
2985 
2986 /*
2987  * @implemented
2988  */
2989 NTSTATUS
2990 NTAPI
2991 MmMapViewInSessionSpace(IN PVOID Section,
2992                         OUT PVOID *MappedBase,
2993                         IN OUT PSIZE_T ViewSize)
2994 {
2995     PAGED_CODE();
2996 
2997     // HACK
2998     if (MiIsRosSectionObject(Section))
2999     {
3000         return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
3001     }
3002 
3003     /* Process must be in a session */
3004     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3005     {
3006         DPRINT1("Process is not in session\n");
3007         return STATUS_NOT_MAPPED_VIEW;
3008     }
3009 
3010     /* Use the system space API, but with the session view instead */
3011     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3012     return MiMapViewInSystemSpace(Section,
3013                                   &MmSessionSpace->Session,
3014                                   MappedBase,
3015                                   ViewSize);
3016 }
3017 
3018 /*
3019  * @implemented
3020  */
3021 NTSTATUS
3022 NTAPI
3023 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
3024 {
3025     PAGED_CODE();
3026 
3027     // HACK
3028     if (!MI_IS_SESSION_ADDRESS(MappedBase))
3029     {
3030         return MmUnmapViewInSystemSpace(MappedBase);
3031     }
3032 
3033     /* Process must be in a session */
3034     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3035     {
3036         DPRINT1("Proess is not in session\n");
3037         return STATUS_NOT_MAPPED_VIEW;
3038     }
3039 
3040     /* Use the system space API, but with the session view instead */
3041     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3042     return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
3043                                     MappedBase);
3044 }
3045 
3046 /*
3047  * @implemented
3048  */
3049 NTSTATUS
3050 NTAPI
3051 MmUnmapViewOfSection(IN PEPROCESS Process,
3052                      IN PVOID BaseAddress)
3053 {
3054     return MiUnmapViewOfSection(Process, BaseAddress, 0);
3055 }
3056 
3057 /*
3058  * @implemented
3059  */
3060 NTSTATUS
3061 NTAPI
3062 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
3063 {
3064     PMEMORY_AREA MemoryArea;
3065     PAGED_CODE();
3066 
3067     /* Was this mapped by RosMm? */
3068     MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
3069     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
3070     {
3071         return MiRosUnmapViewInSystemSpace(MappedBase);
3072     }
3073 
3074     /* It was not, call the ARM3 routine */
3075     return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
3076 }
3077 
3078 /*
3079  * @implemented
3080  */
3081 NTSTATUS
3082 NTAPI
3083 MmCommitSessionMappedView(IN PVOID MappedBase,
3084                           IN SIZE_T ViewSize)
3085 {
3086     ULONG_PTR StartAddress, EndingAddress, Base;
3087     ULONG Hash, Count = 0, Size, QuotaCharge;
3088     PMMSESSION Session;
3089     PMMPTE LastProtoPte, PointerPte, ProtoPte;
3090     PCONTROL_AREA ControlArea;
3091     PSEGMENT Segment;
3092     PSUBSECTION Subsection;
3093     MMPTE TempPte;
3094     PAGED_CODE();
3095 
3096     /* Make sure the base isn't past the session view range */
3097     if ((MappedBase < MiSessionViewStart) ||
3098         (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
3099     {
3100         DPRINT1("Base outside of valid range\n");
3101         return STATUS_INVALID_PARAMETER_1;
3102     }
3103 
3104     /* Make sure the size isn't past the session view range */
3105     if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
3106         (ULONG_PTR)MappedBase) < ViewSize)
3107     {
3108         DPRINT1("Size outside of valid range\n");
3109         return STATUS_INVALID_PARAMETER_2;
3110     }
3111 
3112     /* Sanity check */
3113     ASSERT(ViewSize != 0);
3114 
3115     /* Process must be in a session */
3116     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3117     {
3118         DPRINT1("Process is not in session\n");
3119         return STATUS_NOT_MAPPED_VIEW;
3120     }
3121 
3122     /* Compute the correctly aligned base and end addresses */
3123     StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
3124     EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
3125 
3126     /* Sanity check and grab the session */
3127     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3128     Session = &MmSessionSpace->Session;
3129 
3130     /* Get the hash entry for this allocation */
3131     Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
3132 
3133     /* Lock system space */
3134     KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
3135 
3136     /* Loop twice so we can try rolling over if needed */
3137     while (TRUE)
3138     {
3139         /* Extract the size and base addresses from the entry */
3140         Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
3141         Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
3142 
3143         /* Convert the size to bucket chunks */
3144         Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
3145 
3146         /* Bail out if this entry fits in here */
3147         if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
3148 
3149         /* Check if we overflew past the end of the hash table */
3150         if (++Hash >= Session->SystemSpaceHashSize)
3151         {
3152             /* Reset the hash to zero and keep searching from the bottom */
3153             Hash = 0;
3154             if (++Count == 2)
3155             {
3156                 /* But if we overflew twice, then this is not a real mapping */
3157                 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
3158                              Base,
3159                              2,
3160                              0,
3161                              0);
3162             }
3163         }
3164     }
3165 
3166     /* Make sure the view being mapped is not file-based */
3167     ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
3168     if (ControlArea->FilePointer != NULL)
3169     {
3170         /* It is, so we have to bail out */
3171         DPRINT1("Only page-filed backed sections can be commited\n");
3172         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3173         return STATUS_ALREADY_COMMITTED;
3174     }
3175 
3176     /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
3177     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
3178     ASSERT(ControlArea->u.Flags.Rom == 0);
3179     Subsection = (PSUBSECTION)(ControlArea + 1);
3180 
3181     /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
3182     ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
3183     QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
3184     LastProtoPte = ProtoPte + QuotaCharge;
3185     if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
3186     {
3187         DPRINT1("PTE is out of bounds\n");
3188         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3189         return STATUS_INVALID_PARAMETER_2;
3190     }
3191 
3192     /* Acquire the commit lock and count all the non-committed PTEs */
3193     KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
3194     PointerPte = ProtoPte;
3195     while (PointerPte < LastProtoPte)
3196     {
3197         if (PointerPte->u.Long) QuotaCharge--;
3198         PointerPte++;
3199     }
3200 
3201     /* Was everything committed already? */
3202     if (!QuotaCharge)
3203     {
3204         /* Nothing to do! */
3205         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3206         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3207         return STATUS_SUCCESS;
3208     }
3209 
3210     /* Pick the segment and template PTE */
3211     Segment = ControlArea->Segment;
3212     TempPte = Segment->SegmentPteTemplate;
3213     ASSERT(TempPte.u.Long != 0);
3214 
3215     /* Loop all prototype PTEs to be committed */
3216     PointerPte = ProtoPte;
3217     while (PointerPte < LastProtoPte)
3218     {
3219         /* Make sure the PTE is already invalid */
3220         if (PointerPte->u.Long == 0)
3221         {
3222             /* And write the invalid PTE */
3223             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3224         }
3225 
3226         /* Move to the next PTE */
3227         PointerPte++;
3228     }
3229 
3230     /* Check if we had at least one page charged */
3231     if (QuotaCharge)
3232     {
3233         /* Update the accounting data */
3234         Segment->NumberOfCommittedPages += QuotaCharge;
3235         InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3236     }
3237 
3238     /* Release all */
3239     KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3240     KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3241     return STATUS_SUCCESS;
3242 }
3243 
3244 VOID
3245 NTAPI
3246 MiDeleteARM3Section(PVOID ObjectBody)
3247 {
3248     PSECTION SectionObject;
3249     PCONTROL_AREA ControlArea;
3250     KIRQL OldIrql;
3251 
3252     SectionObject = (PSECTION)ObjectBody;
3253 
3254     if (SectionObject->u.Flags.Based == 1)
3255     {
3256         /* Remove the node from the global section address tree */
3257         KeAcquireGuardedMutex(&MmSectionBasedMutex);
3258         MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot);
3259         KeReleaseGuardedMutex(&MmSectionBasedMutex);
3260     }
3261 
3262     /* Lock the PFN database */
3263     OldIrql = MiAcquirePfnLock();
3264 
3265     ASSERT(SectionObject->Segment);
3266     ASSERT(SectionObject->Segment->ControlArea);
3267 
3268     ControlArea = SectionObject->Segment->ControlArea;
3269 
3270     /* Dereference */
3271     ControlArea->NumberOfSectionReferences--;
3272     ControlArea->NumberOfUserReferences--;
3273 
3274     ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
3275 
3276     /* Check it. It will delete it if there is no more reference to it */
3277     MiCheckControlArea(ControlArea, OldIrql);
3278 }
3279 
3280 ULONG
3281 NTAPI
3282 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer)
3283 {
3284     UNIMPLEMENTED;
3285     return 0;
3286 }
3287 
3288 /* SYSTEM CALLS ***************************************************************/
3289 
3290 NTSTATUS
3291 NTAPI
3292 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3293                         IN PVOID File2MappedAsFile)
3294 {
3295     PVOID AddressSpace;
3296     PMMVAD Vad1, Vad2;
3297     PFILE_OBJECT FileObject1, FileObject2;
3298     NTSTATUS Status;
3299 
3300     /* Lock address space */
3301     AddressSpace = MmGetCurrentAddressSpace();
3302     MmLockAddressSpace(AddressSpace);
3303 
3304     /* Get the VAD for Address 1 */
3305     Vad1 = MiLocateAddress(File1MappedAsAnImage);
3306     if (Vad1 == NULL)
3307     {
3308         /* Fail, the address does not exist */
3309         DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage);
3310         Status = STATUS_INVALID_ADDRESS;
3311         goto Exit;
3312     }
3313 
3314     /* Get the VAD for Address 2 */
3315     Vad2 = MiLocateAddress(File2MappedAsFile);
3316     if (Vad2 == NULL)
3317     {
3318         /* Fail, the address does not exist */
3319         DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile);
3320         Status = STATUS_INVALID_ADDRESS;
3321         goto Exit;
3322     }
3323 
3324     /* Get the file object pointer for VAD 1 */
3325     FileObject1 = MiGetFileObjectForVad(Vad1);
3326     if (FileObject1 == NULL)
3327     {
3328         DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage);
3329         Status = STATUS_CONFLICTING_ADDRESSES;
3330         goto Exit;
3331     }
3332 
3333     /* Get the file object pointer for VAD 2 */
3334     FileObject2 = MiGetFileObjectForVad(Vad2);
3335     if (FileObject2 == NULL)
3336     {
3337         DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile);
3338         Status = STATUS_CONFLICTING_ADDRESSES;
3339         goto Exit;
3340     }
3341 
3342     /* Make sure Vad1 is an image mapping */
3343     if (Vad1->u.VadFlags.VadType != VadImageMap)
3344     {
3345         DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage);
3346         Status = STATUS_NOT_SAME_DEVICE;
3347         goto Exit;
3348     }
3349 
3350     /* SectionObjectPointer is equal if the files are equal */
3351     if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer)
3352     {
3353         Status = STATUS_SUCCESS;
3354     }
3355     else
3356     {
3357         Status = STATUS_NOT_SAME_DEVICE;
3358     }
3359 
3360 Exit:
3361     /* Unlock address space */
3362     MmUnlockAddressSpace(AddressSpace);
3363     return Status;
3364 }
3365 
3366 /*
3367  * @implemented
3368  */
3369 NTSTATUS
3370 NTAPI
3371 NtCreateSection(OUT PHANDLE SectionHandle,
3372                 IN ACCESS_MASK DesiredAccess,
3373                 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3374                 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3375                 IN ULONG SectionPageProtection OPTIONAL,
3376                 IN ULONG AllocationAttributes,
3377                 IN HANDLE FileHandle OPTIONAL)
3378 {
3379     LARGE_INTEGER SafeMaximumSize;
3380     PVOID SectionObject;
3381     HANDLE Handle;
3382     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3383     NTSTATUS Status;
3384     PAGED_CODE();
3385 
3386     /* Check for non-existing flags */
3387     if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3388                                   SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3389                                   SEC_NO_CHANGE)))
3390     {
3391         if (!(AllocationAttributes & 1))
3392         {
3393             DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3394             return STATUS_INVALID_PARAMETER_6;
3395         }
3396     }
3397 
3398     /* Check for no allocation type */
3399     if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3400     {
3401         DPRINT1("Missing allocation type in allocation attributes\n");
3402         return STATUS_INVALID_PARAMETER_6;
3403     }
3404 
3405     /* Check for image allocation with invalid attributes */
3406     if ((AllocationAttributes & SEC_IMAGE) &&
3407         (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3408                                  SEC_NOCACHE | SEC_NO_CHANGE)))
3409     {
3410         DPRINT1("Image allocation with invalid attributes\n");
3411         return STATUS_INVALID_PARAMETER_6;
3412     }
3413 
3414     /* Check for allocation type is both commit and reserve */
3415     if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3416     {
3417         DPRINT1("Commit and reserve in the same time\n");
3418         return STATUS_INVALID_PARAMETER_6;
3419     }
3420 
3421     /* Now check for valid protection */
3422     if ((SectionPageProtection & PAGE_NOCACHE) ||
3423         (SectionPageProtection & PAGE_WRITECOMBINE) ||
3424         (SectionPageProtection & PAGE_GUARD) ||
3425         (SectionPageProtection & PAGE_NOACCESS))
3426     {
3427         DPRINT1("Sections don't support these protections\n");
3428         return STATUS_INVALID_PAGE_PROTECTION;
3429     }
3430 
3431     /* Use a maximum size of zero, if none was specified */
3432     SafeMaximumSize.QuadPart = 0;
3433 
3434     /* Check for user-mode caller */
3435     if (PreviousMode != KernelMode)
3436     {
3437         /* Enter SEH */
3438         _SEH2_TRY
3439         {
3440             /* Safely check user-mode parameters */
3441             if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3442             MaximumSize = &SafeMaximumSize;
3443             ProbeForWriteHandle(SectionHandle);
3444         }
3445         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3446         {
3447             /* Return the exception code */
3448             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3449         }
3450         _SEH2_END;
3451     }
3452     else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
3453 
3454     /* Check that MaximumSize is valid if backed by paging file */
3455     if ((!FileHandle) && (!MaximumSize->QuadPart))
3456         return STATUS_INVALID_PARAMETER_4;
3457 
3458     /* Create the section */
3459     Status = MmCreateSection(&SectionObject,
3460                              DesiredAccess,
3461                              ObjectAttributes,
3462                              MaximumSize,
3463                              SectionPageProtection,
3464                              AllocationAttributes,
3465                              FileHandle,
3466                              NULL);
3467     if (!NT_SUCCESS(Status)) return Status;
3468 
3469     /* FIXME: Should zero last page for a file mapping */
3470 
3471     /* Now insert the object */
3472     Status = ObInsertObject(SectionObject,
3473                             NULL,
3474                             DesiredAccess,
3475                             0,
3476                             NULL,
3477                             &Handle);
3478     if (NT_SUCCESS(Status))
3479     {
3480         /* Enter SEH */
3481         _SEH2_TRY
3482         {
3483             /* Return the handle safely */
3484             *SectionHandle = Handle;
3485         }
3486         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3487         {
3488             /* Nothing here */
3489         }
3490         _SEH2_END;
3491     }
3492 
3493     /* Return the status */
3494     return Status;
3495 }
3496 
3497 NTSTATUS
3498 NTAPI
3499 NtOpenSection(OUT PHANDLE SectionHandle,
3500               IN ACCESS_MASK DesiredAccess,
3501               IN POBJECT_ATTRIBUTES ObjectAttributes)
3502 {
3503     HANDLE Handle;
3504     NTSTATUS Status;
3505     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3506     PAGED_CODE();
3507 
3508     /* Check for user-mode caller */
3509     if (PreviousMode != KernelMode)
3510     {
3511         /* Enter SEH */
3512         _SEH2_TRY
3513         {
3514             /* Safely check user-mode parameters */
3515             ProbeForWriteHandle(SectionHandle);
3516         }
3517         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3518         {
3519             /* Return the exception code */
3520             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3521         }
3522         _SEH2_END;
3523     }
3524 
3525     /* Try opening the object */
3526     Status = ObOpenObjectByName(ObjectAttributes,
3527                                 MmSectionObjectType,
3528                                 PreviousMode,
3529                                 NULL,
3530                                 DesiredAccess,
3531                                 NULL,
3532                                 &Handle);
3533 
3534     /* Enter SEH */
3535     _SEH2_TRY
3536     {
3537         /* Return the handle safely */
3538         *SectionHandle = Handle;
3539     }
3540     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3541     {
3542         /* Nothing here */
3543     }
3544     _SEH2_END;
3545 
3546     /* Return the status */
3547     return Status;
3548 }
3549 
3550 NTSTATUS
3551 NTAPI
3552 NtMapViewOfSection(IN HANDLE SectionHandle,
3553                    IN HANDLE ProcessHandle,
3554                    IN OUT PVOID* BaseAddress,
3555                    IN ULONG_PTR ZeroBits,
3556                    IN SIZE_T CommitSize,
3557                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3558                    IN OUT PSIZE_T ViewSize,
3559                    IN SECTION_INHERIT InheritDisposition,
3560                    IN ULONG AllocationType,
3561                    IN ULONG Protect)
3562 {
3563     PVOID SafeBaseAddress;
3564     LARGE_INTEGER SafeSectionOffset;
3565     SIZE_T SafeViewSize;
3566     PROS_SECTION_OBJECT Section;
3567     PEPROCESS Process;
3568     NTSTATUS Status;
3569     ACCESS_MASK DesiredAccess;
3570     ULONG ProtectionMask;
3571     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3572 #if defined(_M_IX86) || defined(_M_AMD64)
3573     static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES |
3574             MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE);
3575 #else
3576     static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES |
3577             SEC_NO_CHANGE | MEM_RESERVE);
3578 #endif
3579 
3580     /* Check for invalid inherit disposition */
3581     if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
3582     {
3583         DPRINT1("Invalid inherit disposition\n");
3584         return STATUS_INVALID_PARAMETER_8;
3585     }
3586 
3587     /* Allow only valid allocation types */
3588     if (AllocationType & ~ValidAllocationType)
3589     {
3590         DPRINT1("Invalid allocation type\n");
3591         return STATUS_INVALID_PARAMETER_9;
3592     }
3593 
3594     /* Convert the protection mask, and validate it */
3595     ProtectionMask = MiMakeProtectionMask(Protect);
3596     if (ProtectionMask == MM_INVALID_PROTECTION)
3597     {
3598         DPRINT1("Invalid page protection\n");
3599         return STATUS_INVALID_PAGE_PROTECTION;
3600     }
3601 
3602     /* Now convert the protection mask into desired section access mask */
3603     DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
3604 
3605     /* Assume no section offset */
3606     SafeSectionOffset.QuadPart = 0;
3607 
3608     /* Enter SEH */
3609     _SEH2_TRY
3610     {
3611         /* Check for unsafe parameters */
3612         if (PreviousMode != KernelMode)
3613         {
3614             /* Probe the parameters */
3615             ProbeForWritePointer(BaseAddress);
3616             ProbeForWriteSize_t(ViewSize);
3617         }
3618 
3619         /* Check if a section offset was given */
3620         if (SectionOffset)
3621         {
3622             /* Check for unsafe parameters and capture section offset */
3623             if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
3624             SafeSectionOffset = *SectionOffset;
3625         }
3626 
3627         /* Capture the other parameters */
3628         SafeBaseAddress = *BaseAddress;
3629         SafeViewSize = *ViewSize;
3630     }
3631     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3632     {
3633         /* Return the exception code */
3634         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3635     }
3636     _SEH2_END;
3637 
3638     /* Check for kernel-mode address */
3639     if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
3640     {
3641         DPRINT1("Kernel base not allowed\n");
3642         return STATUS_INVALID_PARAMETER_3;
3643     }
3644 
3645     /* Check for range entering kernel-mode */
3646     if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
3647     {
3648         DPRINT1("Overflowing into kernel base not allowed\n");
3649         return STATUS_INVALID_PARAMETER_3;
3650     }
3651 
3652     /* Check for invalid zero bits */
3653     if (ZeroBits)
3654     {
3655         if (ZeroBits > MI_MAX_ZERO_BITS)
3656         {
3657             DPRINT1("Invalid zero bits\n");
3658             return STATUS_INVALID_PARAMETER_4;
3659         }
3660 
3661         if ((((ULONG_PTR)SafeBaseAddress << ZeroBits) >> ZeroBits) != (ULONG_PTR)SafeBaseAddress)
3662         {
3663             DPRINT1("Invalid zero bits\n");
3664             return STATUS_INVALID_PARAMETER_4;
3665         }
3666 
3667         if (((((ULONG_PTR)SafeBaseAddress + SafeViewSize) << ZeroBits) >> ZeroBits) != ((ULONG_PTR)SafeBaseAddress + SafeViewSize))
3668         {
3669             DPRINT1("Invalid zero bits\n");
3670             return STATUS_INVALID_PARAMETER_4;
3671         }
3672     }
3673 
3674     /* Reference the process */
3675     Status = ObReferenceObjectByHandle(ProcessHandle,
3676                                        PROCESS_VM_OPERATION,
3677                                        PsProcessType,
3678                                        PreviousMode,
3679                                        (PVOID*)&Process,
3680                                        NULL);
3681     if (!NT_SUCCESS(Status)) return Status;
3682 
3683     /* Reference the section */
3684     Status = ObReferenceObjectByHandle(SectionHandle,
3685                                        DesiredAccess,
3686                                        MmSectionObjectType,
3687                                        PreviousMode,
3688                                        (PVOID*)&Section,
3689                                        NULL);
3690     if (!NT_SUCCESS(Status))
3691     {
3692         ObDereferenceObject(Process);
3693         return Status;
3694     }
3695 
3696     if (MiIsRosSectionObject(Section) &&
3697         (Section->AllocationAttributes & SEC_PHYSICALMEMORY))
3698     {
3699         if (PreviousMode == UserMode &&
3700             SafeSectionOffset.QuadPart + SafeViewSize > MmHighestPhysicalPage << PAGE_SHIFT)
3701         {
3702             DPRINT1("Denying map past highest physical page.\n");
3703             ObDereferenceObject(Section);
3704             ObDereferenceObject(Process);
3705             return STATUS_INVALID_PARAMETER_6;
3706         }
3707     }
3708     else if (!(AllocationType & MEM_DOS_LIM))
3709     {
3710         /* Check for non-allocation-granularity-aligned BaseAddress */
3711         if (SafeBaseAddress != ALIGN_DOWN_POINTER_BY(SafeBaseAddress, MM_VIRTMEM_GRANULARITY))
3712         {
3713             DPRINT("BaseAddress is not at 64-kilobyte address boundary.\n");
3714             ObDereferenceObject(Section);
3715             ObDereferenceObject(Process);
3716             return STATUS_MAPPED_ALIGNMENT;
3717         }
3718 
3719         /* Do the same for the section offset */
3720         if (SafeSectionOffset.LowPart != ALIGN_DOWN_BY(SafeSectionOffset.LowPart, MM_VIRTMEM_GRANULARITY))
3721         {
3722             DPRINT("SectionOffset is not at 64-kilobyte address boundary.\n");
3723             ObDereferenceObject(Section);
3724             ObDereferenceObject(Process);
3725             return STATUS_MAPPED_ALIGNMENT;
3726         }
3727     }
3728 
3729     /* Now do the actual mapping */
3730     Status = MmMapViewOfSection(Section,
3731                                 Process,
3732                                 &SafeBaseAddress,
3733                                 ZeroBits,
3734                                 CommitSize,
3735                                 &SafeSectionOffset,
3736                                 &SafeViewSize,
3737                                 InheritDisposition,
3738                                 AllocationType,
3739                                 Protect);
3740 
3741     /* Return data only on success */
3742     if (NT_SUCCESS(Status))
3743     {
3744         /* Check if this is an image for the current process */
3745         if (MiIsRosSectionObject(Section) &&
3746             (Section->AllocationAttributes & SEC_IMAGE) &&
3747             (Process == PsGetCurrentProcess()) &&
3748             (Status != STATUS_IMAGE_NOT_AT_BASE))
3749         {
3750             /* Notify the debugger */
3751             DbgkMapViewOfSection(Section,
3752                                  SafeBaseAddress,
3753                                  SafeSectionOffset.LowPart,
3754                                  SafeViewSize);
3755         }
3756 
3757         /* Enter SEH */
3758         _SEH2_TRY
3759         {
3760             /* Return parameters to user */
3761             *BaseAddress = SafeBaseAddress;
3762             *ViewSize = SafeViewSize;
3763             if (SectionOffset) *SectionOffset = SafeSectionOffset;
3764         }
3765         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3766         {
3767             /* Nothing to do */
3768         }
3769         _SEH2_END;
3770     }
3771 
3772     /* Dereference all objects and return status */
3773     ObDereferenceObject(Section);
3774     ObDereferenceObject(Process);
3775     return Status;
3776 }
3777 
3778 NTSTATUS
3779 NTAPI
3780 NtUnmapViewOfSection(IN HANDLE ProcessHandle,
3781                      IN PVOID BaseAddress)
3782 {
3783     PEPROCESS Process;
3784     NTSTATUS Status;
3785     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3786 
3787     /* Don't allowing mapping kernel views */
3788     if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
3789     {
3790         DPRINT1("Trying to unmap a kernel view\n");
3791         return STATUS_NOT_MAPPED_VIEW;
3792     }
3793 
3794     /* Reference the process */
3795     Status = ObReferenceObjectByHandle(ProcessHandle,
3796                                        PROCESS_VM_OPERATION,
3797                                        PsProcessType,
3798                                        PreviousMode,
3799                                        (PVOID*)&Process,
3800                                        NULL);
3801     if (!NT_SUCCESS(Status)) return Status;
3802 
3803     /* Unmap the view */
3804     Status = MiUnmapViewOfSection(Process, BaseAddress, 0);
3805 
3806     /* Dereference the process and return status */
3807     ObDereferenceObject(Process);
3808     return Status;
3809 }
3810 
3811 NTSTATUS
3812 NTAPI
3813 NtExtendSection(IN HANDLE SectionHandle,
3814                 IN OUT PLARGE_INTEGER NewMaximumSize)
3815 {
3816     LARGE_INTEGER SafeNewMaximumSize;
3817     PROS_SECTION_OBJECT Section;
3818     NTSTATUS Status;
3819     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3820 
3821     /* Check for user-mode parameters */
3822     if (PreviousMode != KernelMode)
3823     {
3824         /* Enter SEH */
3825         _SEH2_TRY
3826         {
3827             /* Probe and capture the maximum size, it's both read and write */
3828             ProbeForWriteLargeInteger(NewMaximumSize);
3829             SafeNewMaximumSize = *NewMaximumSize;
3830         }
3831         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3832         {
3833             /* Return the exception code */
3834             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3835         }
3836         _SEH2_END;
3837     }
3838     else
3839     {
3840         /* Just read the size directly */
3841         SafeNewMaximumSize = *NewMaximumSize;
3842     }
3843 
3844     /* Reference the section */
3845     Status = ObReferenceObjectByHandle(SectionHandle,
3846                                        SECTION_EXTEND_SIZE,
3847                                        MmSectionObjectType,
3848                                        PreviousMode,
3849                                        (PVOID*)&Section,
3850                                        NULL);
3851     if (!NT_SUCCESS(Status)) return Status;
3852 
3853     /* Really this should go in MmExtendSection */
3854     if (!(Section->AllocationAttributes & SEC_FILE))
3855     {
3856         DPRINT1("Not extending a file\n");
3857         ObDereferenceObject(Section);
3858         return STATUS_SECTION_NOT_EXTENDED;
3859     }
3860 
3861     /* FIXME: Do the work */
3862 
3863     /* Dereference the section */
3864     ObDereferenceObject(Section);
3865 
3866     /* Enter SEH */
3867     _SEH2_TRY
3868     {
3869         /* Write back the new size */
3870         *NewMaximumSize = SafeNewMaximumSize;
3871     }
3872     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3873     {
3874         /* Nothing to do */
3875     }
3876     _SEH2_END;
3877 
3878     /* Return the status */
3879     return STATUS_NOT_IMPLEMENTED;
3880 }
3881 
3882 /* EOF */
3883