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