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