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