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