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