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