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