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