xref: /reactos/ntoskrnl/mm/ARM3/section.c (revision 5100859e)
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     WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1901     UNICODE_STRING ModuleFileName;
1902     PMEMORY_SECTION_NAME SectionName = NULL;
1903     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1904 
1905     Status = ObReferenceObjectByHandle(ProcessHandle,
1906                                        PROCESS_QUERY_INFORMATION,
1907                                        NULL,
1908                                        PreviousMode,
1909                                        (PVOID*)(&Process),
1910                                        NULL);
1911 
1912     if (!NT_SUCCESS(Status))
1913     {
1914         DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
1915         return Status;
1916     }
1917 
1918     RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1919     Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
1920 
1921     if (NT_SUCCESS(Status))
1922     {
1923         SectionName = MemoryInformation;
1924         if (PreviousMode != KernelMode)
1925         {
1926             _SEH2_TRY
1927             {
1928                 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1929                 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1930                 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1931 
1932                 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1933 
1934             }
1935             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1936             {
1937                 Status = _SEH2_GetExceptionCode();
1938             }
1939             _SEH2_END;
1940         }
1941         else
1942         {
1943             RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1944             SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1945             RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1946 
1947             if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1948 
1949         }
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     PAGED_CODE();
2851 
2852     /* Get the segment and control area */
2853     Section = (PSECTION)SectionObject;
2854     ControlArea = Section->Segment->ControlArea;
2855 
2856     /* These flags/states are not yet supported by ARM3 */
2857     ASSERT(Section->u.Flags.Image == 0);
2858     ASSERT(Section->u.Flags.NoCache == 0);
2859     ASSERT(Section->u.Flags.WriteCombined == 0);
2860     ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2861 
2862     /* FIXME */
2863     if ((AllocationType & MEM_RESERVE) != 0)
2864     {
2865         DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2866         return STATUS_NOT_IMPLEMENTED;
2867     }
2868 
2869     /* Check if the mapping protection is compatible with the create */
2870     if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2871     {
2872         DPRINT1("Mapping protection is incompatible\n");
2873         return STATUS_SECTION_PROTECTION;
2874     }
2875 
2876     /* Check if the offset and size would cause an overflow */
2877     if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2878          (ULONG64)SectionOffset->QuadPart)
2879     {
2880         DPRINT1("Section offset overflows\n");
2881         return STATUS_INVALID_VIEW_SIZE;
2882     }
2883 
2884     /* Check if the offset and size are bigger than the section itself */
2885     if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2886          (ULONG64)Section->SizeOfSection.QuadPart)
2887     {
2888         DPRINT1("Section offset is larger than section\n");
2889         return STATUS_INVALID_VIEW_SIZE;
2890     }
2891 
2892     /* Check if the caller did not specify a view size */
2893     if (!(*ViewSize))
2894     {
2895         /* Compute it for the caller */
2896         *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
2897 
2898         /* Check if it's larger than 4GB or overflows into kernel-mode */
2899         if ((*ViewSize > 0xFFFFFFFF) ||
2900             (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
2901         {
2902             DPRINT1("Section view won't fit\n");
2903             return STATUS_INVALID_VIEW_SIZE;
2904         }
2905     }
2906 
2907     /* Check if the commit size is larger than the view size */
2908     if (CommitSize > *ViewSize)
2909     {
2910         DPRINT1("Attempting to commit more than the view itself\n");
2911         return STATUS_INVALID_PARAMETER_5;
2912     }
2913 
2914     /* Check if the view size is larger than the section */
2915     if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2916     {
2917         DPRINT1("The view is larger than the section\n");
2918         return STATUS_INVALID_VIEW_SIZE;
2919     }
2920 
2921     /* Compute and validate the protection mask */
2922     ProtectionMask = MiMakeProtectionMask(Protect);
2923     if (ProtectionMask == MM_INVALID_PROTECTION)
2924     {
2925         DPRINT1("The protection is invalid\n");
2926         return STATUS_INVALID_PAGE_PROTECTION;
2927     }
2928 
2929     /* We only handle pagefile-backed sections, which cannot be writecombined */
2930     if (Protect & PAGE_WRITECOMBINE)
2931     {
2932         DPRINT1("Cannot write combine a pagefile-backed section\n");
2933         return STATUS_INVALID_PARAMETER_10;
2934     }
2935 
2936     /* Start by attaching to the current process if needed */
2937     if (PsGetCurrentProcess() != Process)
2938     {
2939         KeStackAttachProcess(&Process->Pcb, &ApcState);
2940         Attached = TRUE;
2941     }
2942 
2943     /* Do the actual mapping */
2944     Status = MiMapViewOfDataSection(ControlArea,
2945                                     Process,
2946                                     BaseAddress,
2947                                     SectionOffset,
2948                                     ViewSize,
2949                                     Section,
2950                                     InheritDisposition,
2951                                     ProtectionMask,
2952                                     CommitSize,
2953                                     ZeroBits,
2954                                     AllocationType);
2955 
2956     /* Detach if needed, then return status */
2957     if (Attached) KeUnstackDetachProcess(&ApcState);
2958     return Status;
2959 }
2960 
2961 /*
2962  * @unimplemented
2963  */
2964 BOOLEAN
2965 NTAPI
2966 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2967 {
2968    UNIMPLEMENTED;
2969    return FALSE;
2970 }
2971 
2972 /*
2973  * @unimplemented
2974  */
2975 BOOLEAN
2976 NTAPI
2977 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2978                      IN BOOLEAN DelayClose)
2979 {
2980    UNIMPLEMENTED;
2981    return FALSE;
2982 }
2983 
2984 /*
2985  * @implemented
2986  */
2987 NTSTATUS
2988 NTAPI
2989 MmMapViewInSessionSpace(IN PVOID Section,
2990                         OUT PVOID *MappedBase,
2991                         IN OUT PSIZE_T ViewSize)
2992 {
2993     PAGED_CODE();
2994 
2995     // HACK
2996     if (MiIsRosSectionObject(Section))
2997     {
2998         return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
2999     }
3000 
3001     /* Process must be in a session */
3002     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3003     {
3004         DPRINT1("Process is not in session\n");
3005         return STATUS_NOT_MAPPED_VIEW;
3006     }
3007 
3008     /* Use the system space API, but with the session view instead */
3009     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3010     return MiMapViewInSystemSpace(Section,
3011                                   &MmSessionSpace->Session,
3012                                   MappedBase,
3013                                   ViewSize);
3014 }
3015 
3016 /*
3017  * @implemented
3018  */
3019 NTSTATUS
3020 NTAPI
3021 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
3022 {
3023     PAGED_CODE();
3024 
3025     // HACK
3026     if (!MI_IS_SESSION_ADDRESS(MappedBase))
3027     {
3028         return MmUnmapViewInSystemSpace(MappedBase);
3029     }
3030 
3031     /* Process must be in a session */
3032     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3033     {
3034         DPRINT1("Proess is not in session\n");
3035         return STATUS_NOT_MAPPED_VIEW;
3036     }
3037 
3038     /* Use the system space API, but with the session view instead */
3039     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3040     return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
3041                                     MappedBase);
3042 }
3043 
3044 /*
3045  * @implemented
3046  */
3047 NTSTATUS
3048 NTAPI
3049 MmUnmapViewOfSection(IN PEPROCESS Process,
3050                      IN PVOID BaseAddress)
3051 {
3052     return MiUnmapViewOfSection(Process, BaseAddress, 0);
3053 }
3054 
3055 /*
3056  * @implemented
3057  */
3058 NTSTATUS
3059 NTAPI
3060 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
3061 {
3062     PMEMORY_AREA MemoryArea;
3063     PAGED_CODE();
3064 
3065     /* Was this mapped by RosMm? */
3066     MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
3067     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
3068     {
3069         return MiRosUnmapViewInSystemSpace(MappedBase);
3070     }
3071 
3072     /* It was not, call the ARM3 routine */
3073     return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
3074 }
3075 
3076 /*
3077  * @implemented
3078  */
3079 NTSTATUS
3080 NTAPI
3081 MmCommitSessionMappedView(IN PVOID MappedBase,
3082                           IN SIZE_T ViewSize)
3083 {
3084     ULONG_PTR StartAddress, EndingAddress, Base;
3085     ULONG Hash, Count = 0, Size, QuotaCharge;
3086     PMMSESSION Session;
3087     PMMPTE LastProtoPte, PointerPte, ProtoPte;
3088     PCONTROL_AREA ControlArea;
3089     PSEGMENT Segment;
3090     PSUBSECTION Subsection;
3091     MMPTE TempPte;
3092     PAGED_CODE();
3093 
3094     /* Make sure the base isn't past the session view range */
3095     if ((MappedBase < MiSessionViewStart) ||
3096         (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
3097     {
3098         DPRINT1("Base outside of valid range\n");
3099         return STATUS_INVALID_PARAMETER_1;
3100     }
3101 
3102     /* Make sure the size isn't past the session view range */
3103     if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
3104         (ULONG_PTR)MappedBase) < ViewSize)
3105     {
3106         DPRINT1("Size outside of valid range\n");
3107         return STATUS_INVALID_PARAMETER_2;
3108     }
3109 
3110     /* Sanity check */
3111     ASSERT(ViewSize != 0);
3112 
3113     /* Process must be in a session */
3114     if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3115     {
3116         DPRINT1("Process is not in session\n");
3117         return STATUS_NOT_MAPPED_VIEW;
3118     }
3119 
3120     /* Compute the correctly aligned base and end addresses */
3121     StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
3122     EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
3123 
3124     /* Sanity check and grab the session */
3125     ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3126     Session = &MmSessionSpace->Session;
3127 
3128     /* Get the hash entry for this allocation */
3129     Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
3130 
3131     /* Lock system space */
3132     KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
3133 
3134     /* Loop twice so we can try rolling over if needed */
3135     while (TRUE)
3136     {
3137         /* Extract the size and base addresses from the entry */
3138         Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
3139         Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
3140 
3141         /* Convert the size to bucket chunks */
3142         Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
3143 
3144         /* Bail out if this entry fits in here */
3145         if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
3146 
3147         /* Check if we overflew past the end of the hash table */
3148         if (++Hash >= Session->SystemSpaceHashSize)
3149         {
3150             /* Reset the hash to zero and keep searching from the bottom */
3151             Hash = 0;
3152             if (++Count == 2)
3153             {
3154                 /* But if we overflew twice, then this is not a real mapping */
3155                 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
3156                              Base,
3157                              2,
3158                              0,
3159                              0);
3160             }
3161         }
3162     }
3163 
3164     /* Make sure the view being mapped is not file-based */
3165     ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
3166     if (ControlArea->FilePointer != NULL)
3167     {
3168         /* It is, so we have to bail out */
3169         DPRINT1("Only page-filed backed sections can be commited\n");
3170         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3171         return STATUS_ALREADY_COMMITTED;
3172     }
3173 
3174     /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
3175     ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
3176     ASSERT(ControlArea->u.Flags.Rom == 0);
3177     Subsection = (PSUBSECTION)(ControlArea + 1);
3178 
3179     /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
3180     ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
3181     QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
3182     LastProtoPte = ProtoPte + QuotaCharge;
3183     if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
3184     {
3185         DPRINT1("PTE is out of bounds\n");
3186         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3187         return STATUS_INVALID_PARAMETER_2;
3188     }
3189 
3190     /* Acquire the commit lock and count all the non-committed PTEs */
3191     KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
3192     PointerPte = ProtoPte;
3193     while (PointerPte < LastProtoPte)
3194     {
3195         if (PointerPte->u.Long) QuotaCharge--;
3196         PointerPte++;
3197     }
3198 
3199     /* Was everything committed already? */
3200     if (!QuotaCharge)
3201     {
3202         /* Nothing to do! */
3203         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3204         KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3205         return STATUS_SUCCESS;
3206     }
3207 
3208     /* Pick the segment and template PTE */
3209     Segment = ControlArea->Segment;
3210     TempPte = Segment->SegmentPteTemplate;
3211     ASSERT(TempPte.u.Long != 0);
3212 
3213     /* Loop all prototype PTEs to be committed */
3214     PointerPte = ProtoPte;
3215     while (PointerPte < LastProtoPte)
3216     {
3217         /* Make sure the PTE is already invalid */
3218         if (PointerPte->u.Long == 0)
3219         {
3220             /* And write the invalid PTE */
3221             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3222         }
3223 
3224         /* Move to the next PTE */
3225         PointerPte++;
3226     }
3227 
3228     /* Check if we had at least one page charged */
3229     if (QuotaCharge)
3230     {
3231         /* Update the accounting data */
3232         Segment->NumberOfCommittedPages += QuotaCharge;
3233         InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3234     }
3235 
3236     /* Release all */
3237     KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3238     KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3239     return STATUS_SUCCESS;
3240 }
3241 
3242 VOID
3243 NTAPI
3244 MiDeleteARM3Section(PVOID ObjectBody)
3245 {
3246     PSECTION SectionObject;
3247     PCONTROL_AREA ControlArea;
3248     KIRQL OldIrql;
3249 
3250     SectionObject = (PSECTION)ObjectBody;
3251 
3252     if (SectionObject->u.Flags.Based == 1)
3253     {
3254         /* Remove the node from the global section address tree */
3255         KeAcquireGuardedMutex(&MmSectionBasedMutex);
3256         MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot);
3257         KeReleaseGuardedMutex(&MmSectionBasedMutex);
3258     }
3259 
3260     /* Lock the PFN database */
3261     OldIrql = MiAcquirePfnLock();
3262 
3263     ASSERT(SectionObject->Segment);
3264     ASSERT(SectionObject->Segment->ControlArea);
3265 
3266     ControlArea = SectionObject->Segment->ControlArea;
3267 
3268     /* Dereference */
3269     ControlArea->NumberOfSectionReferences--;
3270     ControlArea->NumberOfUserReferences--;
3271 
3272     ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
3273 
3274     /* Check it. It will delete it if there is no more reference to it */
3275     MiCheckControlArea(ControlArea, OldIrql);
3276 }
3277 
3278 ULONG
3279 NTAPI
3280 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer)
3281 {
3282     UNIMPLEMENTED;
3283     return 0;
3284 }
3285 
3286 /* SYSTEM CALLS ***************************************************************/
3287 
3288 NTSTATUS
3289 NTAPI
3290 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3291                         IN PVOID File2MappedAsFile)
3292 {
3293     PVOID AddressSpace;
3294     PMMVAD Vad1, Vad2;
3295     PFILE_OBJECT FileObject1, FileObject2;
3296     NTSTATUS Status;
3297 
3298     /* Lock address space */
3299     AddressSpace = MmGetCurrentAddressSpace();
3300     MmLockAddressSpace(AddressSpace);
3301 
3302     /* Get the VAD for Address 1 */
3303     Vad1 = MiLocateAddress(File1MappedAsAnImage);
3304     if (Vad1 == NULL)
3305     {
3306         /* Fail, the address does not exist */
3307         DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage);
3308         Status = STATUS_INVALID_ADDRESS;
3309         goto Exit;
3310     }
3311 
3312     /* Get the VAD for Address 2 */
3313     Vad2 = MiLocateAddress(File2MappedAsFile);
3314     if (Vad2 == NULL)
3315     {
3316         /* Fail, the address does not exist */
3317         DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile);
3318         Status = STATUS_INVALID_ADDRESS;
3319         goto Exit;
3320     }
3321 
3322     /* Get the file object pointer for VAD 1 */
3323     FileObject1 = MiGetFileObjectForVad(Vad1);
3324     if (FileObject1 == NULL)
3325     {
3326         DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage);
3327         Status = STATUS_CONFLICTING_ADDRESSES;
3328         goto Exit;
3329     }
3330 
3331     /* Get the file object pointer for VAD 2 */
3332     FileObject2 = MiGetFileObjectForVad(Vad2);
3333     if (FileObject2 == NULL)
3334     {
3335         DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile);
3336         Status = STATUS_CONFLICTING_ADDRESSES;
3337         goto Exit;
3338     }
3339 
3340     /* Make sure Vad1 is an image mapping */
3341     if (Vad1->u.VadFlags.VadType != VadImageMap)
3342     {
3343         DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage);
3344         Status = STATUS_NOT_SAME_DEVICE;
3345         goto Exit;
3346     }
3347 
3348     /* SectionObjectPointer is equal if the files are equal */
3349     if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer)
3350     {
3351         Status = STATUS_SUCCESS;
3352     }
3353     else
3354     {
3355         Status = STATUS_NOT_SAME_DEVICE;
3356     }
3357 
3358 Exit:
3359     /* Unlock address space */
3360     MmUnlockAddressSpace(AddressSpace);
3361     return Status;
3362 }
3363 
3364 /*
3365  * @implemented
3366  */
3367 NTSTATUS
3368 NTAPI
3369 NtCreateSection(OUT PHANDLE SectionHandle,
3370                 IN ACCESS_MASK DesiredAccess,
3371                 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3372                 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3373                 IN ULONG SectionPageProtection OPTIONAL,
3374                 IN ULONG AllocationAttributes,
3375                 IN HANDLE FileHandle OPTIONAL)
3376 {
3377     LARGE_INTEGER SafeMaximumSize;
3378     PVOID SectionObject;
3379     HANDLE Handle;
3380     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3381     NTSTATUS Status;
3382     PAGED_CODE();
3383 
3384     /* Check for non-existing flags */
3385     if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3386                                   SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3387                                   SEC_NO_CHANGE)))
3388     {
3389         if (!(AllocationAttributes & 1))
3390         {
3391             DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3392             return STATUS_INVALID_PARAMETER_6;
3393         }
3394     }
3395 
3396     /* Check for no allocation type */
3397     if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3398     {
3399         DPRINT1("Missing allocation type in allocation attributes\n");
3400         return STATUS_INVALID_PARAMETER_6;
3401     }
3402 
3403     /* Check for image allocation with invalid attributes */
3404     if ((AllocationAttributes & SEC_IMAGE) &&
3405         (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3406                                  SEC_NOCACHE | SEC_NO_CHANGE)))
3407     {
3408         DPRINT1("Image allocation with invalid attributes\n");
3409         return STATUS_INVALID_PARAMETER_6;
3410     }
3411 
3412     /* Check for allocation type is both commit and reserve */
3413     if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3414     {
3415         DPRINT1("Commit and reserve in the same time\n");
3416         return STATUS_INVALID_PARAMETER_6;
3417     }
3418 
3419     /* Now check for valid protection */
3420     if ((SectionPageProtection & PAGE_NOCACHE) ||
3421         (SectionPageProtection & PAGE_WRITECOMBINE) ||
3422         (SectionPageProtection & PAGE_GUARD) ||
3423         (SectionPageProtection & PAGE_NOACCESS))
3424     {
3425         DPRINT1("Sections don't support these protections\n");
3426         return STATUS_INVALID_PAGE_PROTECTION;
3427     }
3428 
3429     /* Use a maximum size of zero, if none was specified */
3430     SafeMaximumSize.QuadPart = 0;
3431 
3432     /* Check for user-mode caller */
3433     if (PreviousMode != KernelMode)
3434     {
3435         /* Enter SEH */
3436         _SEH2_TRY
3437         {
3438             /* Safely check user-mode parameters */
3439             if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3440             MaximumSize = &SafeMaximumSize;
3441             ProbeForWriteHandle(SectionHandle);
3442         }
3443         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3444         {
3445             /* Return the exception code */
3446             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3447         }
3448         _SEH2_END;
3449     }
3450     else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
3451 
3452     /* Check that MaximumSize is valid if backed by paging file */
3453     if ((!FileHandle) && (!MaximumSize->QuadPart))
3454         return STATUS_INVALID_PARAMETER_4;
3455 
3456     /* Create the section */
3457     Status = MmCreateSection(&SectionObject,
3458                              DesiredAccess,
3459                              ObjectAttributes,
3460                              MaximumSize,
3461                              SectionPageProtection,
3462                              AllocationAttributes,
3463                              FileHandle,
3464                              NULL);
3465     if (!NT_SUCCESS(Status)) return Status;
3466 
3467     /* FIXME: Should zero last page for a file mapping */
3468 
3469     /* Now insert the object */
3470     Status = ObInsertObject(SectionObject,
3471                             NULL,
3472                             DesiredAccess,
3473                             0,
3474                             NULL,
3475                             &Handle);
3476     if (NT_SUCCESS(Status))
3477     {
3478         /* Enter SEH */
3479         _SEH2_TRY
3480         {
3481             /* Return the handle safely */
3482             *SectionHandle = Handle;
3483         }
3484         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3485         {
3486             /* Nothing here */
3487         }
3488         _SEH2_END;
3489     }
3490 
3491     /* Return the status */
3492     return Status;
3493 }
3494 
3495 NTSTATUS
3496 NTAPI
3497 NtOpenSection(OUT PHANDLE SectionHandle,
3498               IN ACCESS_MASK DesiredAccess,
3499               IN POBJECT_ATTRIBUTES ObjectAttributes)
3500 {
3501     HANDLE Handle;
3502     NTSTATUS Status;
3503     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3504     PAGED_CODE();
3505 
3506     /* Check for user-mode caller */
3507     if (PreviousMode != KernelMode)
3508     {
3509         /* Enter SEH */
3510         _SEH2_TRY
3511         {
3512             /* Safely check user-mode parameters */
3513             ProbeForWriteHandle(SectionHandle);
3514         }
3515         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3516         {
3517             /* Return the exception code */
3518             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3519         }
3520         _SEH2_END;
3521     }
3522 
3523     /* Try opening the object */
3524     Status = ObOpenObjectByName(ObjectAttributes,
3525                                 MmSectionObjectType,
3526                                 PreviousMode,
3527                                 NULL,
3528                                 DesiredAccess,
3529                                 NULL,
3530                                 &Handle);
3531 
3532     /* Enter SEH */
3533     _SEH2_TRY
3534     {
3535         /* Return the handle safely */
3536         *SectionHandle = Handle;
3537     }
3538     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3539     {
3540         /* Nothing here */
3541     }
3542     _SEH2_END;
3543 
3544     /* Return the status */
3545     return Status;
3546 }
3547 
3548 NTSTATUS
3549 NTAPI
3550 NtMapViewOfSection(IN HANDLE SectionHandle,
3551                    IN HANDLE ProcessHandle,
3552                    IN OUT PVOID* BaseAddress,
3553                    IN ULONG_PTR ZeroBits,
3554                    IN SIZE_T CommitSize,
3555                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3556                    IN OUT PSIZE_T ViewSize,
3557                    IN SECTION_INHERIT InheritDisposition,
3558                    IN ULONG AllocationType,
3559                    IN ULONG Protect)
3560 {
3561     PVOID SafeBaseAddress;
3562     LARGE_INTEGER SafeSectionOffset;
3563     SIZE_T SafeViewSize;
3564     PROS_SECTION_OBJECT Section;
3565     PEPROCESS Process;
3566     NTSTATUS Status;
3567     ACCESS_MASK DesiredAccess;
3568     ULONG ProtectionMask;
3569     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3570 #if defined(_M_IX86) || defined(_M_AMD64)
3571     static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES |
3572             MEM_DOS_LIM | SEC_NO_CHANGE | MEM_RESERVE);
3573 #else
3574     static const ULONG ValidAllocationType = (MEM_TOP_DOWN | MEM_LARGE_PAGES |
3575             SEC_NO_CHANGE | MEM_RESERVE);
3576 #endif
3577 
3578     /* Check for invalid inherit disposition */
3579     if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
3580     {
3581         DPRINT1("Invalid inherit disposition\n");
3582         return STATUS_INVALID_PARAMETER_8;
3583     }
3584 
3585     /* Allow only valid allocation types */
3586     if (AllocationType & ~ValidAllocationType)
3587     {
3588         DPRINT1("Invalid allocation type\n");
3589         return STATUS_INVALID_PARAMETER_9;
3590     }
3591 
3592     /* Convert the protection mask, and validate it */
3593     ProtectionMask = MiMakeProtectionMask(Protect);
3594     if (ProtectionMask == MM_INVALID_PROTECTION)
3595     {
3596         DPRINT1("Invalid page protection\n");
3597         return STATUS_INVALID_PAGE_PROTECTION;
3598     }
3599 
3600     /* Now convert the protection mask into desired section access mask */
3601     DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
3602 
3603     /* Assume no section offset */
3604     SafeSectionOffset.QuadPart = 0;
3605 
3606     /* Enter SEH */
3607     _SEH2_TRY
3608     {
3609         /* Check for unsafe parameters */
3610         if (PreviousMode != KernelMode)
3611         {
3612             /* Probe the parameters */
3613             ProbeForWritePointer(BaseAddress);
3614             ProbeForWriteSize_t(ViewSize);
3615         }
3616 
3617         /* Check if a section offset was given */
3618         if (SectionOffset)
3619         {
3620             /* Check for unsafe parameters and capture section offset */
3621             if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
3622             SafeSectionOffset = *SectionOffset;
3623         }
3624 
3625         /* Capture the other parameters */
3626         SafeBaseAddress = *BaseAddress;
3627         SafeViewSize = *ViewSize;
3628     }
3629     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3630     {
3631         /* Return the exception code */
3632         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3633     }
3634     _SEH2_END;
3635 
3636     /* Check for kernel-mode address */
3637     if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
3638     {
3639         DPRINT1("Kernel base not allowed\n");
3640         return STATUS_INVALID_PARAMETER_3;
3641     }
3642 
3643     /* Check for range entering kernel-mode */
3644     if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
3645     {
3646         DPRINT1("Overflowing into kernel base not allowed\n");
3647         return STATUS_INVALID_PARAMETER_3;
3648     }
3649 
3650     /* Check for invalid zero bits */
3651     if (ZeroBits)
3652     {
3653         if (ZeroBits > MI_MAX_ZERO_BITS)
3654         {
3655             DPRINT1("Invalid zero bits\n");
3656             return STATUS_INVALID_PARAMETER_4;
3657         }
3658 
3659         if ((((ULONG_PTR)SafeBaseAddress << ZeroBits) >> ZeroBits) != (ULONG_PTR)SafeBaseAddress)
3660         {
3661             DPRINT1("Invalid zero bits\n");
3662             return STATUS_INVALID_PARAMETER_4;
3663         }
3664 
3665         if (((((ULONG_PTR)SafeBaseAddress + SafeViewSize) << ZeroBits) >> ZeroBits) != ((ULONG_PTR)SafeBaseAddress + SafeViewSize))
3666         {
3667             DPRINT1("Invalid zero bits\n");
3668             return STATUS_INVALID_PARAMETER_4;
3669         }
3670     }
3671 
3672     /* Reference the process */
3673     Status = ObReferenceObjectByHandle(ProcessHandle,
3674                                        PROCESS_VM_OPERATION,
3675                                        PsProcessType,
3676                                        PreviousMode,
3677                                        (PVOID*)&Process,
3678                                        NULL);
3679     if (!NT_SUCCESS(Status)) return Status;
3680 
3681     /* Reference the section */
3682     Status = ObReferenceObjectByHandle(SectionHandle,
3683                                        DesiredAccess,
3684                                        MmSectionObjectType,
3685                                        PreviousMode,
3686                                        (PVOID*)&Section,
3687                                        NULL);
3688     if (!NT_SUCCESS(Status))
3689     {
3690         ObDereferenceObject(Process);
3691         return Status;
3692     }
3693 
3694     if (MiIsRosSectionObject(Section) &&
3695         (Section->AllocationAttributes & SEC_PHYSICALMEMORY))
3696     {
3697         if (PreviousMode == UserMode &&
3698             SafeSectionOffset.QuadPart + SafeViewSize > MmHighestPhysicalPage << PAGE_SHIFT)
3699         {
3700             DPRINT1("Denying map past highest physical page.\n");
3701             ObDereferenceObject(Section);
3702             ObDereferenceObject(Process);
3703             return STATUS_INVALID_PARAMETER_6;
3704         }
3705     }
3706     else if (!(AllocationType & MEM_DOS_LIM))
3707     {
3708         /* Check for non-allocation-granularity-aligned BaseAddress */
3709         if (SafeBaseAddress != ALIGN_DOWN_POINTER_BY(SafeBaseAddress, MM_VIRTMEM_GRANULARITY))
3710         {
3711             DPRINT("BaseAddress is not at 64-kilobyte address boundary.\n");
3712             ObDereferenceObject(Section);
3713             ObDereferenceObject(Process);
3714             return STATUS_MAPPED_ALIGNMENT;
3715         }
3716 
3717         /* Do the same for the section offset */
3718         if (SafeSectionOffset.LowPart != ALIGN_DOWN_BY(SafeSectionOffset.LowPart, MM_VIRTMEM_GRANULARITY))
3719         {
3720             DPRINT("SectionOffset is not at 64-kilobyte address boundary.\n");
3721             ObDereferenceObject(Section);
3722             ObDereferenceObject(Process);
3723             return STATUS_MAPPED_ALIGNMENT;
3724         }
3725     }
3726 
3727     /* Now do the actual mapping */
3728     Status = MmMapViewOfSection(Section,
3729                                 Process,
3730                                 &SafeBaseAddress,
3731                                 ZeroBits,
3732                                 CommitSize,
3733                                 &SafeSectionOffset,
3734                                 &SafeViewSize,
3735                                 InheritDisposition,
3736                                 AllocationType,
3737                                 Protect);
3738 
3739     /* Return data only on success */
3740     if (NT_SUCCESS(Status))
3741     {
3742         /* Check if this is an image for the current process */
3743         if (MiIsRosSectionObject(Section) &&
3744             (Section->AllocationAttributes & SEC_IMAGE) &&
3745             (Process == PsGetCurrentProcess()) &&
3746             (Status != STATUS_IMAGE_NOT_AT_BASE))
3747         {
3748             /* Notify the debugger */
3749             DbgkMapViewOfSection(Section,
3750                                  SafeBaseAddress,
3751                                  SafeSectionOffset.LowPart,
3752                                  SafeViewSize);
3753         }
3754 
3755         /* Enter SEH */
3756         _SEH2_TRY
3757         {
3758             /* Return parameters to user */
3759             *BaseAddress = SafeBaseAddress;
3760             *ViewSize = SafeViewSize;
3761             if (SectionOffset) *SectionOffset = SafeSectionOffset;
3762         }
3763         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3764         {
3765             /* Nothing to do */
3766         }
3767         _SEH2_END;
3768     }
3769 
3770     /* Dereference all objects and return status */
3771     ObDereferenceObject(Section);
3772     ObDereferenceObject(Process);
3773     return Status;
3774 }
3775 
3776 NTSTATUS
3777 NTAPI
3778 NtUnmapViewOfSection(IN HANDLE ProcessHandle,
3779                      IN PVOID BaseAddress)
3780 {
3781     PEPROCESS Process;
3782     NTSTATUS Status;
3783     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3784 
3785     /* Don't allowing mapping kernel views */
3786     if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
3787     {
3788         DPRINT1("Trying to unmap a kernel view\n");
3789         return STATUS_NOT_MAPPED_VIEW;
3790     }
3791 
3792     /* Reference the process */
3793     Status = ObReferenceObjectByHandle(ProcessHandle,
3794                                        PROCESS_VM_OPERATION,
3795                                        PsProcessType,
3796                                        PreviousMode,
3797                                        (PVOID*)&Process,
3798                                        NULL);
3799     if (!NT_SUCCESS(Status)) return Status;
3800 
3801     /* Unmap the view */
3802     Status = MiUnmapViewOfSection(Process, BaseAddress, 0);
3803 
3804     /* Dereference the process and return status */
3805     ObDereferenceObject(Process);
3806     return Status;
3807 }
3808 
3809 NTSTATUS
3810 NTAPI
3811 NtExtendSection(IN HANDLE SectionHandle,
3812                 IN OUT PLARGE_INTEGER NewMaximumSize)
3813 {
3814     LARGE_INTEGER SafeNewMaximumSize;
3815     PROS_SECTION_OBJECT Section;
3816     NTSTATUS Status;
3817     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3818 
3819     /* Check for user-mode parameters */
3820     if (PreviousMode != KernelMode)
3821     {
3822         /* Enter SEH */
3823         _SEH2_TRY
3824         {
3825             /* Probe and capture the maximum size, it's both read and write */
3826             ProbeForWriteLargeInteger(NewMaximumSize);
3827             SafeNewMaximumSize = *NewMaximumSize;
3828         }
3829         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3830         {
3831             /* Return the exception code */
3832             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3833         }
3834         _SEH2_END;
3835     }
3836     else
3837     {
3838         /* Just read the size directly */
3839         SafeNewMaximumSize = *NewMaximumSize;
3840     }
3841 
3842     /* Reference the section */
3843     Status = ObReferenceObjectByHandle(SectionHandle,
3844                                        SECTION_EXTEND_SIZE,
3845                                        MmSectionObjectType,
3846                                        PreviousMode,
3847                                        (PVOID*)&Section,
3848                                        NULL);
3849     if (!NT_SUCCESS(Status)) return Status;
3850 
3851     /* Really this should go in MmExtendSection */
3852     if (!(Section->AllocationAttributes & SEC_FILE))
3853     {
3854         DPRINT1("Not extending a file\n");
3855         ObDereferenceObject(Section);
3856         return STATUS_SECTION_NOT_EXTENDED;
3857     }
3858 
3859     /* FIXME: Do the work */
3860 
3861     /* Dereference the section */
3862     ObDereferenceObject(Section);
3863 
3864     /* Enter SEH */
3865     _SEH2_TRY
3866     {
3867         /* Write back the new size */
3868         *NewMaximumSize = SafeNewMaximumSize;
3869     }
3870     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3871     {
3872         /* Nothing to do */
3873     }
3874     _SEH2_END;
3875 
3876     /* Return the status */
3877     return STATUS_NOT_IMPLEMENTED;
3878 }
3879 
3880 /* EOF */
3881