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