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
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
18 /* GLOBALS ********************************************************************/
20 ACCESS_MASK MmMakeSectionAccess
[8] =
25 SECTION_MAP_EXECUTE
| SECTION_MAP_READ
,
28 SECTION_MAP_EXECUTE
| SECTION_MAP_WRITE
,
29 SECTION_MAP_EXECUTE
| SECTION_MAP_READ
32 ACCESS_MASK MmMakeFileAccess
[8] =
37 FILE_EXECUTE
| FILE_READ_DATA
,
38 FILE_WRITE_DATA
| FILE_READ_DATA
,
40 FILE_EXECUTE
| FILE_WRITE_DATA
| FILE_READ_DATA
,
41 FILE_EXECUTE
| FILE_READ_DATA
44 CHAR MmUserProtectionToMask1
[16] =
49 (CHAR
)MM_INVALID_PROTECTION
,
51 (CHAR
)MM_INVALID_PROTECTION
,
52 (CHAR
)MM_INVALID_PROTECTION
,
53 (CHAR
)MM_INVALID_PROTECTION
,
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
64 CHAR MmUserProtectionToMask2
[16] =
69 (CHAR
)MM_INVALID_PROTECTION
,
71 (CHAR
)MM_INVALID_PROTECTION
,
72 (CHAR
)MM_INVALID_PROTECTION
,
73 (CHAR
)MM_INVALID_PROTECTION
,
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
84 ULONG MmCompatibleProtectionMask
[8] =
88 PAGE_NOACCESS
| PAGE_READONLY
| PAGE_WRITECOPY
,
90 PAGE_NOACCESS
| PAGE_EXECUTE
,
92 PAGE_NOACCESS
| PAGE_READONLY
| PAGE_WRITECOPY
| PAGE_EXECUTE
|
95 PAGE_NOACCESS
| PAGE_READONLY
| PAGE_WRITECOPY
| PAGE_READWRITE
,
97 PAGE_NOACCESS
| PAGE_READONLY
| PAGE_WRITECOPY
,
99 PAGE_NOACCESS
| PAGE_READONLY
| PAGE_WRITECOPY
| PAGE_READWRITE
|
100 PAGE_EXECUTE
| PAGE_EXECUTE_READ
| PAGE_EXECUTE_READWRITE
|
101 PAGE_EXECUTE_WRITECOPY
,
103 PAGE_NOACCESS
| PAGE_READONLY
| PAGE_WRITECOPY
| PAGE_EXECUTE
|
104 PAGE_EXECUTE_READ
| PAGE_EXECUTE_WRITECOPY
108 KGUARDED_MUTEX MmSectionCommitMutex
;
109 MM_AVL_TABLE MmSectionBasedRoot
;
110 KGUARDED_MUTEX MmSectionBasedMutex
;
111 PVOID MmHighSectionBase
;
113 /* PRIVATE FUNCTIONS **********************************************************/
117 MiIsProtectionCompatible(IN ULONG SectionPageProtection
,
118 IN ULONG NewSectionPageProtection
)
120 ULONG ProtectionMask
, CompatibleMask
;
122 /* Calculate the protection mask and make sure it's valid */
123 ProtectionMask
= MiMakeProtectionMask(SectionPageProtection
);
124 if (ProtectionMask
== MM_INVALID_PROTECTION
)
126 DPRINT1("Invalid protection mask\n");
130 /* Calculate the compatible mask */
131 CompatibleMask
= MmCompatibleProtectionMask
[ProtectionMask
& 0x7] |
132 PAGE_GUARD
| PAGE_NOCACHE
| PAGE_WRITECOMBINE
;
134 /* See if the mapping protection is compatible with the create protection */
135 return ((CompatibleMask
| NewSectionPageProtection
) == CompatibleMask
);
140 MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection
)
142 ULONG ProtectionMask
;
144 /* Calculate the protection mask and make sure it's valid */
145 ProtectionMask
= MiMakeProtectionMask(SectionPageProtection
);
146 if (ProtectionMask
== MM_INVALID_PROTECTION
)
148 DPRINT1("Invalid protection mask\n");
149 return STATUS_INVALID_PAGE_PROTECTION
;
152 /* Now convert it to the required file access */
153 return MmMakeFileAccess
[ProtectionMask
& 0x7];
158 MiMakeProtectionMask(IN ULONG Protect
)
160 ULONG Mask1
, Mask2
, ProtectMask
;
162 /* PAGE_EXECUTE_WRITECOMBINE is theoretically the maximum */
163 if (Protect
>= (PAGE_WRITECOMBINE
* 2)) return MM_INVALID_PROTECTION
;
166 * Windows API protection mask can be understood as two bitfields, differing
167 * by whether or not execute rights are being requested
169 Mask1
= Protect
& 0xF;
170 Mask2
= (Protect
>> 4) & 0xF;
172 /* Check which field is there */
175 /* Mask2 must be there, use it to determine the PTE protection */
176 if (!Mask2
) return MM_INVALID_PROTECTION
;
177 ProtectMask
= MmUserProtectionToMask2
[Mask2
];
181 /* Mask2 should not be there, use Mask1 to determine the PTE mask */
182 if (Mask2
) return MM_INVALID_PROTECTION
;
183 ProtectMask
= MmUserProtectionToMask1
[Mask1
];
186 /* Make sure the final mask is a valid one */
187 if (ProtectMask
== MM_INVALID_PROTECTION
) return MM_INVALID_PROTECTION
;
189 /* Check for PAGE_GUARD option */
190 if (Protect
& PAGE_GUARD
)
192 /* It's not valid on no-access, nocache, or writecombine pages */
193 if ((ProtectMask
== MM_NOACCESS
) ||
194 (Protect
& (PAGE_NOCACHE
| PAGE_WRITECOMBINE
)))
196 /* Fail such requests */
197 return MM_INVALID_PROTECTION
;
200 /* This actually turns on guard page in this scenario! */
201 ProtectMask
|= MM_GUARDPAGE
;
204 /* Check for nocache option */
205 if (Protect
& PAGE_NOCACHE
)
207 /* The earlier check should've eliminated this possibility */
208 ASSERT((Protect
& PAGE_GUARD
) == 0);
210 /* Check for no-access page or write combine page */
211 if ((ProtectMask
== MM_NOACCESS
) || (Protect
& PAGE_WRITECOMBINE
))
213 /* Such a request is invalid */
214 return MM_INVALID_PROTECTION
;
217 /* Add the PTE flag */
218 ProtectMask
|= MM_NOCACHE
;
221 /* Check for write combine option */
222 if (Protect
& PAGE_WRITECOMBINE
)
224 /* The two earlier scenarios should've caught this */
225 ASSERT((Protect
& (PAGE_GUARD
| PAGE_NOACCESS
)) == 0);
227 /* Don't allow on no-access pages */
228 if (ProtectMask
== MM_NOACCESS
) return MM_INVALID_PROTECTION
;
230 /* This actually turns on write-combine in this scenario! */
231 ProtectMask
|= MM_NOACCESS
;
234 /* Return the final MM PTE protection mask */
240 MiInitializeSystemSpaceMap(IN PMMSESSION InputSession OPTIONAL
)
242 SIZE_T AllocSize
, BitmapSize
, Size
;
246 /* Check if this a session or system space */
249 /* Use the input session */
250 Session
= InputSession
;
251 ViewStart
= MiSessionViewStart
;
252 Size
= MmSessionViewSize
;
256 /* Use the system space "session" */
257 Session
= &MmSession
;
258 ViewStart
= MiSystemViewStart
;
259 Size
= MmSystemViewSize
;
262 /* Initialize the system space lock */
263 Session
->SystemSpaceViewLockPointer
= &Session
->SystemSpaceViewLock
;
264 KeInitializeGuardedMutex(Session
->SystemSpaceViewLockPointer
);
266 /* Set the start address */
267 Session
->SystemSpaceViewStart
= ViewStart
;
269 /* Create a bitmap to describe system space */
270 BitmapSize
= sizeof(RTL_BITMAP
) + ((((Size
/ MI_SYSTEM_VIEW_BUCKET_SIZE
) + 31) / 32) * sizeof(ULONG
));
271 Session
->SystemSpaceBitMap
= ExAllocatePoolWithTag(NonPagedPool
,
274 ASSERT(Session
->SystemSpaceBitMap
);
275 RtlInitializeBitMap(Session
->SystemSpaceBitMap
,
276 (PULONG
)(Session
->SystemSpaceBitMap
+ 1),
277 (ULONG
)(Size
/ MI_SYSTEM_VIEW_BUCKET_SIZE
));
279 /* Set system space fully empty to begin with */
280 RtlClearAllBits(Session
->SystemSpaceBitMap
);
282 /* Set default hash flags */
283 Session
->SystemSpaceHashSize
= 31;
284 Session
->SystemSpaceHashKey
= Session
->SystemSpaceHashSize
- 1;
285 Session
->SystemSpaceHashEntries
= 0;
287 /* Calculate how much space for the hash views we'll need */
288 AllocSize
= sizeof(MMVIEW
) * Session
->SystemSpaceHashSize
;
289 ASSERT(AllocSize
< PAGE_SIZE
);
291 /* Allocate and zero the view table */
292 Session
->SystemSpaceViewTable
= ExAllocatePoolWithTag(Session
== &MmSession
?
297 ASSERT(Session
->SystemSpaceViewTable
!= NULL
);
298 RtlZeroMemory(Session
->SystemSpaceViewTable
, AllocSize
);
306 MiInsertInSystemSpace(IN PMMSESSION Session
,
308 IN PCONTROL_AREA ControlArea
)
311 ULONG Entry
, Hash
, i
, HashSize
;
315 /* Stay within 4GB */
316 ASSERT(Buckets
< MI_SYSTEM_VIEW_BUCKET_SIZE
);
318 /* Lock system space */
319 KeAcquireGuardedMutex(Session
->SystemSpaceViewLockPointer
);
321 /* Check if we're going to exhaust hash entries */
322 if ((Session
->SystemSpaceHashEntries
+ 8) > Session
->SystemSpaceHashSize
)
324 /* Double the hash size */
325 HashSize
= Session
->SystemSpaceHashSize
* 2;
327 /* Save the old table and allocate a new one */
328 OldTable
= Session
->SystemSpaceViewTable
;
329 Session
->SystemSpaceViewTable
= ExAllocatePoolWithTag(Session
==
336 if (!Session
->SystemSpaceViewTable
)
338 /* Failed to allocate a new table, keep the old one for now */
339 Session
->SystemSpaceViewTable
= OldTable
;
343 /* Clear the new table and set the new ahsh and key */
344 RtlZeroMemory(Session
->SystemSpaceViewTable
, HashSize
* sizeof(MMVIEW
));
345 Session
->SystemSpaceHashSize
= HashSize
;
346 Session
->SystemSpaceHashKey
= Session
->SystemSpaceHashSize
- 1;
348 /* Loop the old table */
349 for (i
= 0; i
< Session
->SystemSpaceHashSize
/ 2; i
++)
351 /* Check if the entry was valid */
352 if (OldTable
[i
].Entry
)
354 /* Re-hash the old entry and search for space in the new table */
355 Hash
= (OldTable
[i
].Entry
>> 16) % Session
->SystemSpaceHashKey
;
356 while (Session
->SystemSpaceViewTable
[Hash
].Entry
)
358 /* Loop back at the beginning if we had an overflow */
359 if (++Hash
>= Session
->SystemSpaceHashSize
) Hash
= 0;
362 /* Write the old entry in the new table */
363 Session
->SystemSpaceViewTable
[Hash
] = OldTable
[i
];
367 /* Free the old table */
368 ExFreePool(OldTable
);
372 /* Check if we ran out */
373 if (Session
->SystemSpaceHashEntries
== Session
->SystemSpaceHashSize
)
375 DPRINT1("Ran out of system view hash entries\n");
376 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
380 /* Find space where to map this view */
381 i
= RtlFindClearBitsAndSet(Session
->SystemSpaceBitMap
, Buckets
, 0);
384 /* Out of space, fail */
385 Session
->BitmapFailures
++;
386 DPRINT1("Out of system view space\n");
387 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
391 /* Compute the base address */
392 Base
= (PVOID
)((ULONG_PTR
)Session
->SystemSpaceViewStart
+ (i
* MI_SYSTEM_VIEW_BUCKET_SIZE
));
394 /* Get the hash entry for this allocation */
395 Entry
= ((ULONG_PTR
)Base
& ~(MI_SYSTEM_VIEW_BUCKET_SIZE
- 1)) + Buckets
;
396 Hash
= (Entry
>> 16) % Session
->SystemSpaceHashKey
;
398 /* Loop hash entries until a free one is found */
399 while (Session
->SystemSpaceViewTable
[Hash
].Entry
)
401 /* Unless we overflow, in which case loop back at hash o */
402 if (++Hash
>= Session
->SystemSpaceHashSize
) Hash
= 0;
405 /* Add this entry into the hash table */
406 Session
->SystemSpaceViewTable
[Hash
].Entry
= Entry
;
407 Session
->SystemSpaceViewTable
[Hash
].ControlArea
= ControlArea
;
409 /* Hash entry found, increment total and return the base address */
410 Session
->SystemSpaceHashEntries
++;
411 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
417 MiAddMappedPtes(IN PMMPTE FirstPte
,
418 IN PFN_NUMBER PteCount
,
419 IN PCONTROL_AREA ControlArea
)
422 PMMPTE PointerPte
, ProtoPte
, LastProtoPte
, LastPte
;
423 PSUBSECTION Subsection
;
425 /* ARM3 doesn't support this yet */
426 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
427 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
428 ASSERT(ControlArea
->FilePointer
== NULL
);
431 ASSERT(PteCount
!= 0);
432 ASSERT(ControlArea
->NumberOfMappedViews
>= 1);
433 ASSERT(ControlArea
->NumberOfUserReferences
>= 1);
434 ASSERT(ControlArea
->NumberOfSectionReferences
!= 0);
435 ASSERT(ControlArea
->u
.Flags
.BeingCreated
== 0);
436 ASSERT(ControlArea
->u
.Flags
.BeingDeleted
== 0);
437 ASSERT(ControlArea
->u
.Flags
.BeingPurged
== 0);
439 /* Get the PTEs for the actual mapping */
440 PointerPte
= FirstPte
;
441 LastPte
= FirstPte
+ PteCount
;
443 /* Get the prototype PTEs that desribe the section mapping in the subsection */
444 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
445 ProtoPte
= Subsection
->SubsectionBase
;
446 LastProtoPte
= &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
];
448 /* Loop the PTEs for the mapping */
449 while (PointerPte
< LastPte
)
451 /* We may have run out of prototype PTEs in this subsection */
452 if (ProtoPte
>= LastProtoPte
)
454 /* But we don't handle this yet */
458 /* The PTE should be completely clear */
459 ASSERT(PointerPte
->u
.Long
== 0);
461 /* Build the prototype PTE and write it */
462 MI_MAKE_PROTOTYPE_PTE(&TempPte
, ProtoPte
);
463 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
470 /* No failure path */
471 return STATUS_SUCCESS
;
476 MiFillSystemPageDirectory(IN PVOID Base
,
477 IN SIZE_T NumberOfBytes
)
479 PMMPDE PointerPde
, LastPde
, SystemMapPde
;
481 PFN_NUMBER PageFrameIndex
, ParentPage
;
485 /* Find the PDEs needed for this mapping */
486 PointerPde
= MiAddressToPde(Base
);
487 LastPde
= MiAddressToPde((PVOID
)((ULONG_PTR
)Base
+ NumberOfBytes
- 1));
489 #if (_MI_PAGING_LEVELS == 2)
490 /* Find the system double-mapped PDE that describes this mapping */
491 SystemMapPde
= &MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)];
493 /* We don't have a double mapping */
494 SystemMapPde
= PointerPde
;
497 /* Use the PDE template and loop the PDEs */
498 TempPde
= ValidKernelPde
;
499 while (PointerPde
<= LastPde
)
501 /* Lock the PFN database */
502 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
504 /* Check if we don't already have this PDE mapped */
505 if (SystemMapPde
->u
.Hard
.Valid
== 0)
507 /* Grab a page for it */
508 MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
509 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
510 PageFrameIndex
= MiRemoveZeroPage(MI_GET_NEXT_COLOR());
511 ASSERT(PageFrameIndex
);
512 TempPde
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
514 #if (_MI_PAGING_LEVELS == 2)
515 ParentPage
= MmSystemPageDirectory
[(PointerPde
- MiAddressToPde(NULL
)) / PDE_COUNT
];
517 ParentPage
= MiPdeToPpe(PointerPde
)->u
.Hard
.PageFrameNumber
;
519 /* Initialize its PFN entry, with the parent system page directory page table */
520 MiInitializePfnForOtherProcess(PageFrameIndex
,
524 /* Make the system PDE entry valid */
525 MI_WRITE_VALID_PDE(SystemMapPde
, TempPde
);
527 /* The system PDE entry might be the PDE itself, so check for this */
528 if (PointerPde
->u
.Hard
.Valid
== 0)
530 /* It's different, so make the real PDE valid too */
531 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
535 /* Release the lock and keep going with the next PDE */
536 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
544 MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea
,
545 IN BOOLEAN FailIfSystemViews
)
549 /* Flag not yet supported */
550 ASSERT(FailIfSystemViews
== FALSE
);
552 /* Lock the PFN database */
553 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
555 /* State not yet supported */
556 ASSERT(ControlArea
->u
.Flags
.BeingPurged
== 0);
558 /* Increase the reference counts */
559 ControlArea
->NumberOfMappedViews
++;
560 ControlArea
->NumberOfUserReferences
++;
561 ASSERT(ControlArea
->NumberOfSectionReferences
!= 0);
563 /* Release the PFN lock and return success */
564 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
565 return STATUS_SUCCESS
;
570 MiLocateSubsection(IN PMMVAD Vad
,
573 PSUBSECTION Subsection
;
574 PCONTROL_AREA ControlArea
;
577 /* Get the control area */
578 ControlArea
= Vad
->ControlArea
;
579 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
580 ASSERT(ControlArea
->u
.Flags
.Image
== 0);
581 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
583 /* Get the subsection */
584 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
586 /* We only support single-subsection segments */
587 ASSERT(Subsection
->SubsectionBase
!= NULL
);
588 ASSERT(Vad
->FirstPrototypePte
>= Subsection
->SubsectionBase
);
589 ASSERT(Vad
->FirstPrototypePte
< &Subsection
->SubsectionBase
[Subsection
->PtesInSubsection
]);
591 /* Compute the PTE offset */
592 PteOffset
= Vpn
- Vad
->StartingVpn
;
593 PteOffset
+= Vad
->FirstPrototypePte
- Subsection
->SubsectionBase
;
595 /* Again, we only support single-subsection segments */
596 ASSERT(PteOffset
< 0xF0000000);
597 ASSERT(PteOffset
< Subsection
->PtesInSubsection
);
599 /* Return the subsection */
605 MiSegmentDelete(IN PSEGMENT Segment
)
607 PCONTROL_AREA ControlArea
;
608 SEGMENT_FLAGS SegmentFlags
;
609 PSUBSECTION Subsection
;
610 PMMPTE PointerPte
, LastPte
, PteForProto
;
612 PFN_NUMBER PageFrameIndex
;
617 SegmentFlags
= Segment
->SegmentFlags
;
618 ControlArea
= Segment
->ControlArea
;
620 /* Make sure control area is on the right delete path */
621 ASSERT(ControlArea
->u
.Flags
.BeingDeleted
== 1);
622 ASSERT(ControlArea
->WritableUserReferences
== 0);
624 /* These things are not supported yet */
625 ASSERT(ControlArea
->DereferenceList
.Flink
== NULL
);
626 ASSERT(!(ControlArea
->u
.Flags
.Image
) && !(ControlArea
->u
.Flags
.File
));
627 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
628 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
630 /* Get the subsection and PTEs for this segment */
631 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
632 PointerPte
= Subsection
->SubsectionBase
;
633 LastPte
= PointerPte
+ Segment
->NonExtendedPtes
;
635 /* Lock the PFN database */
636 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
638 /* Check if the master PTE is invalid */
639 PteForProto
= MiAddressToPte(PointerPte
);
640 if (!PteForProto
->u
.Hard
.Valid
)
643 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
646 /* Loop all the segment PTEs */
647 while (PointerPte
< LastPte
)
649 /* Check if it's time to switch master PTEs if we passed a PDE boundary */
650 if (!((ULONG_PTR
)PointerPte
& (PD_SIZE
- 1)) &&
651 (PointerPte
!= Subsection
->SubsectionBase
))
653 /* Check if the master PTE is invalid */
654 PteForProto
= MiAddressToPte(PointerPte
);
655 if (!PteForProto
->u
.Hard
.Valid
)
658 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
662 /* This should be a prototype PTE */
663 TempPte
= *PointerPte
;
664 ASSERT(SegmentFlags
.LargePages
== 0);
665 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
667 /* See if we should clean things up */
668 if (!(ControlArea
->u
.Flags
.Image
) && !(ControlArea
->u
.Flags
.File
))
671 * This is a section backed by the pagefile. Now that it doesn't exist anymore,
672 * we can give everything back to the system.
674 ASSERT(TempPte
.u
.Soft
.Prototype
== 0);
676 if (TempPte
.u
.Soft
.Transition
== 1)
678 /* We can give the page back for other use */
679 DPRINT("Releasing page for transition PTE %p\n", PointerPte
);
680 PageFrameIndex
= PFN_FROM_PTE(&TempPte
);
681 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
683 /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */
684 ASSERT(Pfn1
->u3
.ReferenceCount
== 0);
686 /* And it should be in standby or modified list */
687 ASSERT((Pfn1
->u3
.e1
.PageLocation
== ModifiedPageList
) || (Pfn1
->u3
.e1
.PageLocation
== StandbyPageList
));
689 /* Unlink it and put it back in free list */
690 MiUnlinkPageFromList(Pfn1
);
692 /* Temporarily mark this as active and make it free again */
693 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
694 MI_SET_PFN_DELETED(Pfn1
);
696 MiInsertPageInFreeList(PageFrameIndex
);
698 else if (TempPte
.u
.Soft
.PageFileHigh
!= 0)
700 /* Should not happen for now */
706 /* unsupported for now */
709 /* File-backed section must have prototype PTEs */
710 ASSERT(TempPte
.u
.Soft
.Prototype
== 1);
713 /* Zero the PTE and keep going */
714 PointerPte
->u
.Long
= 0;
718 /* Release the PFN lock */
719 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
721 /* Free the structures */
722 ExFreePool(ControlArea
);
728 MiCheckControlArea(IN PCONTROL_AREA ControlArea
,
731 BOOLEAN DeleteSegment
= FALSE
;
732 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
734 /* Check if this is the last reference or view */
735 if (!(ControlArea
->NumberOfMappedViews
) &&
736 !(ControlArea
->NumberOfSectionReferences
))
738 /* There should be no more user references either */
739 ASSERT(ControlArea
->NumberOfUserReferences
== 0);
741 /* Not yet supported */
742 ASSERT(ControlArea
->FilePointer
== NULL
);
744 /* The control area is being destroyed */
745 ControlArea
->u
.Flags
.BeingDeleted
= TRUE
;
746 DeleteSegment
= TRUE
;
749 /* Release the PFN lock */
750 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
752 /* Delete the segment if needed */
755 /* No more user write references at all */
756 ASSERT(ControlArea
->WritableUserReferences
== 0);
757 MiSegmentDelete(ControlArea
->Segment
);
763 MiDereferenceControlArea(IN PCONTROL_AREA ControlArea
)
767 /* Lock the PFN database */
768 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
770 /* Drop reference counts */
771 ControlArea
->NumberOfMappedViews
--;
772 ControlArea
->NumberOfUserReferences
--;
774 /* Check if it's time to delete the CA. This releases the lock */
775 MiCheckControlArea(ControlArea
, OldIrql
);
780 MiRemoveMappedView(IN PEPROCESS CurrentProcess
,
784 PCONTROL_AREA ControlArea
;
785 PETHREAD CurrentThread
= PsGetCurrentThread();
787 /* Get the control area */
788 ControlArea
= Vad
->ControlArea
;
790 /* We only support non-extendable, non-image, pagefile-backed regular sections */
791 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
792 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== FALSE
);
794 ASSERT(ControlArea
->FilePointer
== NULL
);
796 /* Delete the actual virtual memory pages */
797 MiDeleteVirtualAddresses(Vad
->StartingVpn
<< PAGE_SHIFT
,
798 (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1),
801 /* Release the working set */
802 MiUnlockProcessWorkingSetUnsafe(CurrentProcess
, CurrentThread
);
804 /* Lock the PFN database */
805 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
807 /* Remove references */
808 ControlArea
->NumberOfMappedViews
--;
809 ControlArea
->NumberOfUserReferences
--;
811 /* Check if it should be destroyed */
812 MiCheckControlArea(ControlArea
, OldIrql
);
817 MiUnmapViewOfSection(IN PEPROCESS Process
,
818 IN PVOID BaseAddress
,
821 PMEMORY_AREA MemoryArea
;
822 BOOLEAN Attached
= FALSE
;
825 PVOID DbgBase
= NULL
;
828 PETHREAD CurrentThread
= PsGetCurrentThread();
829 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
832 /* Check for Mm Region */
833 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, BaseAddress
);
834 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
837 return MiRosUnmapViewOfSection(Process
, BaseAddress
, Flags
);
840 /* Check if we should attach to the process */
841 if (CurrentProcess
!= Process
)
843 /* The process is different, do an attach */
844 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
848 /* Check if we need to lock the address space */
849 if (!Flags
) MmLockAddressSpace(&Process
->Vm
);
851 /* Check if the process is already daed */
852 if (Process
->VmDeleted
)
855 DPRINT1("Process died!\n");
856 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
857 Status
= STATUS_PROCESS_IS_TERMINATING
;
861 /* Find the VAD for the address and make sure it's a section VAD */
862 Vad
= MiLocateAddress(BaseAddress
);
863 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
))
865 /* Couldn't find it, or invalid VAD, fail */
866 DPRINT1("No VAD or invalid VAD\n");
867 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
868 Status
= STATUS_NOT_MAPPED_VIEW
;
872 /* We should be attached */
873 ASSERT(Process
== PsGetCurrentProcess());
875 /* We need the base address for the debugger message on image-backed VADs */
876 if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
878 DbgBase
= (PVOID
)(Vad
->StartingVpn
>> PAGE_SHIFT
);
881 /* Compute the size of the VAD region */
882 RegionSize
= PAGE_SIZE
+ ((Vad
->EndingVpn
- Vad
->StartingVpn
) << PAGE_SHIFT
);
884 /* For SEC_NO_CHANGE sections, we need some extra checks */
885 if (Vad
->u
.VadFlags
.NoChange
== 1)
887 /* Are we allowed to mess with this VAD? */
888 Status
= MiCheckSecuredVad(Vad
,
889 (PVOID
)(Vad
->StartingVpn
>> PAGE_SHIFT
),
892 if (!NT_SUCCESS(Status
))
895 DPRINT1("Trying to unmap protected VAD!\n");
896 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
901 /* Not currently supported */
902 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
904 /* FIXME: Remove VAD charges */
906 /* Lock the working set */
907 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
910 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
911 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
913 /* Remove the PTEs for this view, which also releases the working set lock */
914 MiRemoveMappedView(Process
, Vad
);
916 /* FIXME: Remove commitment */
918 /* Update performance counter and release the lock */
919 Process
->VirtualSize
-= RegionSize
;
920 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
922 /* Destroy the VAD and return success */
924 Status
= STATUS_SUCCESS
;
926 /* Failure and success case -- send debugger message, detach, and return */
928 if (DbgBase
) DbgkUnMapViewOfSection(DbgBase
);
929 if (Attached
) KeUnstackDetachProcess(&ApcState
);
935 MiSessionCommitPageTables(IN PVOID StartVa
,
940 PMMPDE StartPde
, EndPde
;
941 MMPDE TempPde
= ValidKernelPdeLocal
;
943 PFN_NUMBER PageCount
= 0, ActualPages
= 0, PageFrameNumber
;
945 /* Windows sanity checks */
946 ASSERT(StartVa
>= (PVOID
)MmSessionBase
);
947 ASSERT(EndVa
< (PVOID
)MiSessionSpaceEnd
);
948 ASSERT(PAGE_ALIGN(EndVa
) == EndVa
);
950 /* Get the start and end PDE, then loop each one */
951 StartPde
= MiAddressToPde(StartVa
);
952 EndPde
= MiAddressToPde((PVOID
)((ULONG_PTR
)EndVa
- 1));
953 Index
= ((ULONG_PTR
)StartVa
- (ULONG_PTR
)MmSessionBase
) >> 22;
954 while (StartPde
<= EndPde
)
957 /* If we don't already have a page table for it, increment count */
958 if (MmSessionSpace
->PageTables
[Index
].u
.Long
== 0) PageCount
++;
960 /* Move to the next one */
965 /* If there's no page tables to create, bail out */
966 if (PageCount
== 0) return STATUS_SUCCESS
;
968 /* Reset the start PDE and index */
969 StartPde
= MiAddressToPde(StartVa
);
970 Index
= ((ULONG_PTR
)StartVa
- (ULONG_PTR
)MmSessionBase
) >> 22;
972 /* Loop each PDE while holding the working set lock */
973 // MiLockWorkingSet(PsGetCurrentThread(),
974 // &MmSessionSpace->GlobalVirtualAddress->Vm);
976 _WARN("MiSessionCommitPageTables halfplemented for amd64")
977 DBG_UNREFERENCED_LOCAL_VARIABLE(OldIrql
);
978 DBG_UNREFERENCED_LOCAL_VARIABLE(Color
);
979 DBG_UNREFERENCED_LOCAL_VARIABLE(TempPde
);
980 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn1
);
981 DBG_UNREFERENCED_LOCAL_VARIABLE(PageFrameNumber
);
984 while (StartPde
<= EndPde
)
986 /* Check if we already have a page table */
987 if (MmSessionSpace
->PageTables
[Index
].u
.Long
== 0)
989 /* We don't, so the PDE shouldn't be ready yet */
990 ASSERT(StartPde
->u
.Hard
.Valid
== 0);
992 /* ReactOS check to avoid MiEnsureAvailablePageOrWait */
993 ASSERT(MmAvailablePages
>= 32);
995 /* Acquire the PFN lock and grab a zero page */
996 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
997 Color
= (++MmSessionSpace
->Color
) & MmSecondaryColorMask
;
998 PageFrameNumber
= MiRemoveZeroPage(Color
);
999 TempPde
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
1000 MI_WRITE_VALID_PDE(StartPde
, TempPde
);
1002 /* Write the page table in session space structure */
1003 ASSERT(MmSessionSpace
->PageTables
[Index
].u
.Long
== 0);
1004 MmSessionSpace
->PageTables
[Index
] = TempPde
;
1006 /* Initialize the PFN */
1007 MiInitializePfnForOtherProcess(PageFrameNumber
,
1009 MmSessionSpace
->SessionPageDirectoryIndex
);
1011 /* And now release the lock */
1012 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1014 /* Get the PFN entry and make sure there's no event for it */
1015 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
1016 ASSERT(Pfn1
->u1
.Event
== NULL
);
1018 /* Increment the number of pages */
1022 /* Move to the next PDE */
1028 /* Make sure we didn't do more pages than expected */
1029 ASSERT(ActualPages
<= PageCount
);
1031 /* Release the working set lock */
1032 // MiUnlockWorkingSet(PsGetCurrentThread(),
1033 // &MmSessionSpace->GlobalVirtualAddress->Vm);
1036 /* If we did at least one page... */
1039 /* Update the performance counters! */
1040 InterlockedExchangeAddSizeT(&MmSessionSpace
->NonPageablePages
, ActualPages
);
1041 InterlockedExchangeAddSizeT(&MmSessionSpace
->CommittedPages
, ActualPages
);
1045 return STATUS_SUCCESS
;
1050 MiMapViewInSystemSpace(IN PVOID Section
,
1051 IN PMMSESSION Session
,
1052 OUT PVOID
*MappedBase
,
1053 IN OUT PSIZE_T ViewSize
)
1056 PCONTROL_AREA ControlArea
;
1057 ULONG Buckets
, SectionSize
;
1061 /* Get the control area, check for any flags ARM3 doesn't yet support */
1062 ControlArea
= ((PSECTION
)Section
)->Segment
->ControlArea
;
1063 ASSERT(ControlArea
->u
.Flags
.Image
== 0);
1064 ASSERT(ControlArea
->FilePointer
== NULL
);
1065 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
1066 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
1067 ASSERT(ControlArea
->u
.Flags
.WasPurged
== 0);
1069 /* Increase the reference and map count on the control area, no purges yet */
1070 Status
= MiCheckPurgeAndUpMapCount(ControlArea
, FALSE
);
1071 ASSERT(NT_SUCCESS(Status
));
1073 /* Get the section size at creation time */
1074 SectionSize
= ((PSECTION
)Section
)->SizeOfSection
.LowPart
;
1076 /* If the caller didn't specify a view size, assume the whole section */
1077 if (!(*ViewSize
)) *ViewSize
= SectionSize
;
1079 /* Check if the caller wanted a larger section than the view */
1080 if (*ViewSize
> SectionSize
)
1083 DPRINT1("View is too large\n");
1084 MiDereferenceControlArea(ControlArea
);
1085 return STATUS_INVALID_VIEW_SIZE
;
1088 /* Get the number of 64K buckets required for this mapping */
1089 Buckets
= (ULONG
)(*ViewSize
/ MI_SYSTEM_VIEW_BUCKET_SIZE
);
1090 if (*ViewSize
& (MI_SYSTEM_VIEW_BUCKET_SIZE
- 1)) Buckets
++;
1092 /* Check if the view is more than 4GB large */
1093 if (Buckets
>= MI_SYSTEM_VIEW_BUCKET_SIZE
)
1096 DPRINT1("View is too large\n");
1097 MiDereferenceControlArea(ControlArea
);
1098 return STATUS_INVALID_VIEW_SIZE
;
1101 /* Insert this view into system space and get a base address for it */
1102 Base
= MiInsertInSystemSpace(Session
, Buckets
, ControlArea
);
1106 DPRINT1("Out of system space\n");
1107 MiDereferenceControlArea(ControlArea
);
1108 return STATUS_NO_MEMORY
;
1111 /* What's the underlying session? */
1112 if (Session
== &MmSession
)
1114 /* Create the PDEs needed for this mapping, and double-map them if needed */
1115 MiFillSystemPageDirectory(Base
, Buckets
* MI_SYSTEM_VIEW_BUCKET_SIZE
);
1116 Status
= STATUS_SUCCESS
;
1120 /* Create the PDEs needed for this mapping */
1121 Status
= MiSessionCommitPageTables(Base
,
1122 (PVOID
)((ULONG_PTR
)Base
+
1123 Buckets
* MI_SYSTEM_VIEW_BUCKET_SIZE
));
1124 ASSERT(NT_SUCCESS(Status
));
1127 /* Create the actual prototype PTEs for this mapping */
1128 Status
= MiAddMappedPtes(MiAddressToPte(Base
),
1129 BYTES_TO_PAGES(*ViewSize
),
1131 ASSERT(NT_SUCCESS(Status
));
1133 /* Return the base adress of the mapping and success */
1135 return STATUS_SUCCESS
;
1140 MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea
)
1144 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1146 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1147 ControlArea
->u
.Flags
.DebugSymbolsLoaded
|= 1;
1149 ASSERT(OldIrql
<= APC_LEVEL
);
1150 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1151 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
1156 MiLoadUserSymbols(IN PCONTROL_AREA ControlArea
,
1157 IN PVOID BaseAddress
,
1158 IN PEPROCESS Process
)
1161 ANSI_STRING FileNameA
;
1162 PLIST_ENTRY NextEntry
;
1163 PUNICODE_STRING FileName
;
1164 PIMAGE_NT_HEADERS NtHeaders
;
1165 PLDR_DATA_TABLE_ENTRY LdrEntry
;
1167 FileName
= &ControlArea
->FilePointer
->FileName
;
1168 if (FileName
->Length
== 0)
1173 /* Acquire module list lock */
1174 KeEnterCriticalRegion();
1175 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource
, TRUE
);
1177 /* Browse list to try to find current module */
1178 for (NextEntry
= MmLoadedUserImageList
.Flink
;
1179 NextEntry
!= &MmLoadedUserImageList
;
1180 NextEntry
= NextEntry
->Flink
)
1183 LdrEntry
= CONTAINING_RECORD(NextEntry
,
1184 LDR_DATA_TABLE_ENTRY
,
1187 /* If already in the list, increase load count */
1188 if (LdrEntry
->DllBase
== BaseAddress
)
1190 ++LdrEntry
->LoadCount
;
1195 /* Not in the list, we'll add it */
1196 if (NextEntry
== &MmLoadedUserImageList
)
1198 /* Allocate our element, taking to the name string and its null char */
1199 LdrEntry
= ExAllocatePoolWithTag(NonPagedPool
, FileName
->Length
+ sizeof(UNICODE_NULL
) + sizeof(*LdrEntry
), 'bDmM');
1202 memset(LdrEntry
, 0, FileName
->Length
+ sizeof(UNICODE_NULL
) + sizeof(*LdrEntry
));
1206 /* Get image checksum and size */
1207 NtHeaders
= RtlImageNtHeader(BaseAddress
);
1210 LdrEntry
->SizeOfImage
= NtHeaders
->OptionalHeader
.SizeOfImage
;
1211 LdrEntry
->CheckSum
= NtHeaders
->OptionalHeader
.CheckSum
;
1214 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1216 ExFreePoolWithTag(LdrEntry
, 'bDmM');
1217 _SEH2_YIELD(return);
1221 /* Fill all the details */
1222 LdrEntry
->DllBase
= BaseAddress
;
1223 LdrEntry
->FullDllName
.Buffer
= (PVOID
)((ULONG_PTR
)LdrEntry
+ sizeof(*LdrEntry
));
1224 LdrEntry
->FullDllName
.Length
= FileName
->Length
;
1225 LdrEntry
->FullDllName
.MaximumLength
= FileName
->Length
+ sizeof(UNICODE_NULL
);
1226 memcpy(LdrEntry
->FullDllName
.Buffer
, FileName
->Buffer
, FileName
->Length
);
1227 LdrEntry
->FullDllName
.Buffer
[LdrEntry
->FullDllName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1228 LdrEntry
->LoadCount
= 1;
1231 InsertHeadList(&MmLoadedUserImageList
, &LdrEntry
->InLoadOrderLinks
);
1236 ExReleaseResourceLite(&PsLoadedModuleResource
);
1237 KeLeaveCriticalRegion();
1240 Status
= RtlUnicodeStringToAnsiString(&FileNameA
, FileName
, TRUE
);
1241 if (NT_SUCCESS(Status
))
1243 DbgLoadImageSymbols(&FileNameA
, BaseAddress
, (ULONG_PTR
)Process
->UniqueProcessId
);
1244 RtlFreeAnsiString(&FileNameA
);
1250 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea
,
1251 IN PEPROCESS Process
,
1252 IN PVOID
*BaseAddress
,
1253 IN PLARGE_INTEGER SectionOffset
,
1254 IN PSIZE_T ViewSize
,
1255 IN PSECTION Section
,
1256 IN SECTION_INHERIT InheritDisposition
,
1257 IN ULONG ProtectionMask
,
1258 IN SIZE_T CommitSize
,
1259 IN ULONG_PTR ZeroBits
,
1260 IN ULONG AllocationType
)
1263 ULONG_PTR StartAddress
;
1264 ULONG_PTR ViewSizeInPages
;
1265 PSUBSECTION Subsection
;
1267 PFN_NUMBER PteOffset
;
1269 ULONG QuotaCharge
= 0, QuotaExcess
= 0;
1270 PMMPTE PointerPte
, LastPte
;
1272 ULONG Granularity
= MM_VIRTMEM_GRANULARITY
;
1274 DPRINT("Mapping ARM3 data section\n");
1276 /* Get the segment for this section */
1277 Segment
= ControlArea
->Segment
;
1280 /* ALlow being less restrictive on x86. */
1281 if (AllocationType
& MEM_DOS_LIM
)
1282 Granularity
= PAGE_SIZE
;
1285 /* One can only reserve a file-based mapping, not shared memory! */
1286 if ((AllocationType
& MEM_RESERVE
) && !(ControlArea
->FilePointer
))
1288 return STATUS_INVALID_PARAMETER_9
;
1291 /* First, increase the map count. No purging is supported yet */
1292 Status
= MiCheckPurgeAndUpMapCount(ControlArea
, FALSE
);
1293 if (!NT_SUCCESS(Status
)) return Status
;
1295 /* Check if the caller specified the view size */
1298 /* The caller did not, so pick a 64K aligned view size based on the offset */
1299 SectionOffset
->LowPart
&= ~(_64K
- 1);
1300 *ViewSize
= (SIZE_T
)(Section
->SizeOfSection
.QuadPart
- SectionOffset
->QuadPart
);
1304 /* A size was specified, align it to a 64K boundary */
1305 *ViewSize
+= SectionOffset
->LowPart
& (_64K
- 1);
1307 /* Align the offset as well to make this an aligned map */
1308 SectionOffset
->LowPart
&= ~((ULONG
)_64K
- 1);
1311 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1312 ASSERT((SectionOffset
->LowPart
& ((ULONG
)_64K
- 1)) == 0);
1314 /* It's illegal to try to map more than overflows a LONG_PTR */
1315 if (*ViewSize
>= MAXLONG_PTR
)
1317 MiDereferenceControlArea(ControlArea
);
1318 return STATUS_INVALID_VIEW_SIZE
;
1321 /* Windows ASSERTs for this flag */
1322 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
1324 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1325 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
1326 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
1328 /* Sections with extended segments are not supported in ARM3 */
1329 ASSERT(Segment
->SegmentFlags
.TotalNumberOfPtes4132
== 0);
1331 /* Within this section, figure out which PTEs will describe the view */
1332 PteOffset
= (PFN_NUMBER
)(SectionOffset
->QuadPart
>> PAGE_SHIFT
);
1334 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1335 ASSERT(PteOffset
< Segment
->TotalNumberOfPtes
);
1336 ASSERT(((SectionOffset
->QuadPart
+ *ViewSize
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
) >= PteOffset
);
1338 /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1339 ASSERT(PteOffset
< Subsection
->PtesInSubsection
);
1341 /* In ARM3, only page-file backed sections (shared memory) are supported now */
1342 ASSERT(ControlArea
->FilePointer
== NULL
);
1344 /* Windows ASSERTs for this too -- there must be a subsection base address */
1345 ASSERT(Subsection
->SubsectionBase
!= NULL
);
1347 /* Compute how much commit space the segment will take */
1348 if ((CommitSize
) && (Segment
->NumberOfCommittedPages
< Segment
->TotalNumberOfPtes
))
1350 /* Charge for the maximum pages */
1351 QuotaCharge
= BYTES_TO_PAGES(CommitSize
);
1354 /* ARM3 does not currently support large pages */
1355 ASSERT(Segment
->SegmentFlags
.LargePages
== 0);
1357 /* Calculate how many pages the region spans */
1358 ViewSizeInPages
= BYTES_TO_PAGES(*ViewSize
);
1360 /* A VAD can now be allocated. Do so and zero it out */
1361 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1362 ASSERT((AllocationType
& MEM_RESERVE
) == 0); /* ARM3 does not support this */
1363 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'ldaV');
1366 MiDereferenceControlArea(ControlArea
);
1367 return STATUS_INSUFFICIENT_RESOURCES
;
1370 RtlZeroMemory(Vad
, sizeof(MMVAD_LONG
));
1371 Vad
->u4
.Banked
= (PVOID
)0xDEADBABE;
1373 /* Write all the data required in the VAD for handling a fault */
1374 Vad
->ControlArea
= ControlArea
;
1375 Vad
->u
.VadFlags
.CommitCharge
= 0;
1376 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
1377 Vad
->u2
.VadFlags2
.FileOffset
= (ULONG
)(SectionOffset
->QuadPart
>> 16);
1378 Vad
->u2
.VadFlags2
.Inherit
= (InheritDisposition
== ViewShare
);
1379 if ((AllocationType
& SEC_NO_CHANGE
) || (Section
->u
.Flags
.NoChange
))
1381 /* This isn't really implemented yet, but handle setting the flag */
1382 Vad
->u
.VadFlags
.NoChange
= 1;
1383 Vad
->u2
.VadFlags2
.SecNoChange
= 1;
1386 /* Finally, write down the first and last prototype PTE */
1387 Vad
->FirstPrototypePte
= &Subsection
->SubsectionBase
[PteOffset
];
1388 PteOffset
+= ViewSizeInPages
- 1;
1389 ASSERT(PteOffset
< Subsection
->PtesInSubsection
);
1390 Vad
->LastContiguousPte
= &Subsection
->SubsectionBase
[PteOffset
];
1392 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1393 ASSERT(Vad
->FirstPrototypePte
<= Vad
->LastContiguousPte
);
1395 /* FIXME: Should setup VAD bitmap */
1396 Status
= STATUS_SUCCESS
;
1398 /* Check if anything was committed */
1401 /* Set the start and end PTE addresses, and pick the template PTE */
1402 PointerPte
= Vad
->FirstPrototypePte
;
1403 LastPte
= PointerPte
+ BYTES_TO_PAGES(CommitSize
);
1404 TempPte
= Segment
->SegmentPteTemplate
;
1406 /* Acquire the commit lock and loop all prototype PTEs to be committed */
1407 KeAcquireGuardedMutex(&MmSectionCommitMutex
);
1408 while (PointerPte
< LastPte
)
1410 /* Make sure the PTE is already invalid */
1411 if (PointerPte
->u
.Long
== 0)
1413 /* And write the invalid PTE */
1414 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1418 /* The PTE is valid, so skip it */
1422 /* Move to the next PTE */
1426 /* Now check how many pages exactly we committed, and update accounting */
1427 ASSERT(QuotaCharge
>= QuotaExcess
);
1428 QuotaCharge
-= QuotaExcess
;
1429 Segment
->NumberOfCommittedPages
+= QuotaCharge
;
1430 ASSERT(Segment
->NumberOfCommittedPages
<= Segment
->TotalNumberOfPtes
);
1432 /* Now that we're done, release the lock */
1433 KeReleaseGuardedMutex(&MmSectionCommitMutex
);
1436 /* Is it SEC_BASED, or did the caller manually specify an address? */
1437 if (*BaseAddress
!= NULL
)
1439 /* Just align what the caller gave us */
1440 StartAddress
= ALIGN_DOWN_BY((ULONG_PTR
)*BaseAddress
, Granularity
);
1442 else if (Section
->Address
.StartingVpn
!= 0)
1444 /* It is a SEC_BASED mapping, use the address that was generated */
1445 StartAddress
= Section
->Address
.StartingVpn
+ SectionOffset
->LowPart
;
1452 /* Insert the VAD */
1453 Status
= MiInsertVadEx((PMMVAD
)Vad
,
1455 ViewSizeInPages
* PAGE_SIZE
,
1456 MAXULONG_PTR
>> ZeroBits
,
1459 if (!NT_SUCCESS(Status
))
1464 /* Windows stores this for accounting purposes, do so as well */
1465 if (!Segment
->u2
.FirstMappedVa
) Segment
->u2
.FirstMappedVa
= (PVOID
)StartAddress
;
1467 /* Finally, let the caller know where, and for what size, the view was mapped */
1468 *ViewSize
= ViewSizeInPages
* PAGE_SIZE
;
1469 *BaseAddress
= (PVOID
)StartAddress
;
1470 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress
, *ViewSize
);
1471 return STATUS_SUCCESS
;
1476 MiSubsectionConsistent(IN PSUBSECTION Subsection
)
1478 /* ReactOS only supports systems with 4K pages and 4K sectors */
1479 ASSERT(Subsection
->u
.SubsectionFlags
.SectorEndOffset
== 0);
1481 /* Therefore, then number of PTEs should be equal to the number of sectors */
1482 if (Subsection
->NumberOfFullSectors
!= Subsection
->PtesInSubsection
)
1484 /* Break and warn if this is inconsistent */
1485 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1486 Subsection
->NumberOfFullSectors
, Subsection
->PtesInSubsection
);
1493 MiCreateDataFileMap(IN PFILE_OBJECT File
,
1494 OUT PSEGMENT
*Segment
,
1495 IN PSIZE_T MaximumSize
,
1496 IN ULONG SectionPageProtection
,
1497 IN ULONG AllocationAttributes
,
1498 IN ULONG IgnoreFileSizing
)
1500 /* Not yet implemented */
1503 return STATUS_NOT_IMPLEMENTED
;
1508 MiCreatePagingFileMap(OUT PSEGMENT
*Segment
,
1509 IN PSIZE_T MaximumSize
,
1510 IN ULONG ProtectionMask
,
1511 IN ULONG AllocationAttributes
)
1517 PCONTROL_AREA ControlArea
;
1518 PSEGMENT NewSegment
;
1519 PSUBSECTION Subsection
;
1522 /* No large pages in ARM3 yet */
1523 ASSERT((AllocationAttributes
& SEC_LARGE_PAGES
) == 0);
1525 /* Pagefile-backed sections need a known size */
1526 if (!(*MaximumSize
)) return STATUS_INVALID_PARAMETER_4
;
1528 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1529 SizeLimit
= MAXULONG_PTR
- sizeof(SEGMENT
);
1530 SizeLimit
/= sizeof(MMPTE
);
1531 SizeLimit
<<= PAGE_SHIFT
;
1533 /* Fail if this size is too big */
1534 if (*MaximumSize
> SizeLimit
) return STATUS_SECTION_TOO_BIG
;
1536 /* Calculate how many Prototype PTEs will be needed */
1537 PteCount
= (PFN_COUNT
)((*MaximumSize
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
);
1539 /* For commited memory, we must have a valid protection mask */
1540 if (AllocationAttributes
& SEC_COMMIT
) ASSERT(ProtectionMask
!= 0);
1542 /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1543 NewSegment
= ExAllocatePoolWithTag(PagedPool
,
1545 sizeof(MMPTE
) * (PteCount
- 1),
1548 *Segment
= NewSegment
;
1550 /* Now allocate the control area, which has the subsection structure */
1551 ControlArea
= ExAllocatePoolWithTag(NonPagedPool
,
1552 sizeof(CONTROL_AREA
) + sizeof(SUBSECTION
),
1554 ASSERT(ControlArea
);
1556 /* And zero it out, filling the basic segmnet pointer and reference fields */
1557 RtlZeroMemory(ControlArea
, sizeof(CONTROL_AREA
) + sizeof(SUBSECTION
));
1558 ControlArea
->Segment
= NewSegment
;
1559 ControlArea
->NumberOfSectionReferences
= 1;
1560 ControlArea
->NumberOfUserReferences
= 1;
1562 /* Convert allocation attributes to control area flags */
1563 if (AllocationAttributes
& SEC_BASED
) ControlArea
->u
.Flags
.Based
= 1;
1564 if (AllocationAttributes
& SEC_RESERVE
) ControlArea
->u
.Flags
.Reserve
= 1;
1565 if (AllocationAttributes
& SEC_COMMIT
) ControlArea
->u
.Flags
.Commit
= 1;
1567 /* We just allocated it */
1568 ControlArea
->u
.Flags
.BeingCreated
= 1;
1570 /* The subsection follows, write the mask, PTE count and point back to the CA */
1571 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
1572 Subsection
->ControlArea
= ControlArea
;
1573 Subsection
->PtesInSubsection
= PteCount
;
1574 Subsection
->u
.SubsectionFlags
.Protection
= ProtectionMask
;
1576 /* Zero out the segment's prototype PTEs, and link it with the control area */
1577 PointerPte
= &NewSegment
->ThePtes
[0];
1578 RtlZeroMemory(NewSegment
, sizeof(SEGMENT
));
1579 NewSegment
->PrototypePte
= PointerPte
;
1580 NewSegment
->ControlArea
= ControlArea
;
1582 /* Save some extra accounting data for the segment as well */
1583 NewSegment
->u1
.CreatingProcess
= PsGetCurrentProcess();
1584 NewSegment
->SizeOfSegment
= PteCount
* PAGE_SIZE
;
1585 NewSegment
->TotalNumberOfPtes
= PteCount
;
1586 NewSegment
->NonExtendedPtes
= PteCount
;
1588 /* The subsection's base address is the first Prototype PTE in the segment */
1589 Subsection
->SubsectionBase
= PointerPte
;
1591 /* Start with an empty PTE, unless this is a commit operation */
1593 if (AllocationAttributes
& SEC_COMMIT
)
1595 /* In which case, write down the protection mask in the Prototype PTEs */
1596 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
1598 /* For accounting, also mark these pages as being committed */
1599 NewSegment
->NumberOfCommittedPages
= PteCount
;
1602 /* The template PTE itself for the segment should also have the mask set */
1603 NewSegment
->SegmentPteTemplate
.u
.Soft
.Protection
= ProtectionMask
;
1605 /* Write out the prototype PTEs, for now they're simply demand zero */
1607 RtlFillMemoryUlonglong(PointerPte
, PteCount
* sizeof(MMPTE
), TempPte
.u
.Long
);
1609 RtlFillMemoryUlong(PointerPte
, PteCount
* sizeof(MMPTE
), TempPte
.u
.Long
);
1611 return STATUS_SUCCESS
;
1616 MiGetFileObjectForSectionAddress(
1618 OUT PFILE_OBJECT
*FileObject
)
1621 PCONTROL_AREA ControlArea
;
1624 Vad
= MiLocateAddress(Address
);
1627 /* Fail, the address does not exist */
1628 DPRINT1("Invalid address\n");
1629 return STATUS_INVALID_ADDRESS
;
1632 /* Check if this is a RosMm memory area */
1633 if (Vad
->u
.VadFlags
.Spare
!= 0)
1635 PMEMORY_AREA MemoryArea
= (PMEMORY_AREA
)Vad
;
1636 PROS_SECTION_OBJECT Section
;
1638 /* Check if it's a section view (RosMm section) */
1639 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1641 /* Get the section pointer to the SECTION_OBJECT */
1642 Section
= MemoryArea
->Data
.SectionData
.Section
;
1643 *FileObject
= Section
->FileObject
;
1647 ASSERT(MemoryArea
->Type
== MEMORY_AREA_CACHE
);
1648 DPRINT1("Address is a cache section!\n");
1649 return STATUS_SECTION_NOT_IMAGE
;
1654 /* Make sure it's not a VM VAD */
1655 if (Vad
->u
.VadFlags
.PrivateMemory
== 1)
1657 DPRINT1("Address is not a section\n");
1658 return STATUS_SECTION_NOT_IMAGE
;
1661 /* Get the control area */
1662 ControlArea
= Vad
->ControlArea
;
1663 if (!(ControlArea
) || !(ControlArea
->u
.Flags
.Image
))
1665 DPRINT1("Address is not a section\n");
1666 return STATUS_SECTION_NOT_IMAGE
;
1669 /* Get the file object */
1670 *FileObject
= ControlArea
->FilePointer
;
1673 /* Return success */
1674 return STATUS_SUCCESS
;
1679 MmGetFileObjectForSection(IN PVOID SectionObject
)
1681 PSECTION_OBJECT Section
;
1682 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1683 ASSERT(SectionObject
!= NULL
);
1685 /* Check if it's an ARM3, or ReactOS section */
1686 if (MiIsRosSectionObject(SectionObject
) == FALSE
)
1688 /* Return the file pointer stored in the control area */
1689 Section
= SectionObject
;
1690 return Section
->Segment
->ControlArea
->FilePointer
;
1693 /* Return the file object */
1694 return ((PROS_SECTION_OBJECT
)SectionObject
)->FileObject
;
1699 MiGetFileObjectForVad(
1702 PCONTROL_AREA ControlArea
;
1703 PFILE_OBJECT FileObject
;
1705 /* Check if this is a RosMm memory area */
1706 if (Vad
->u
.VadFlags
.Spare
!= 0)
1708 PMEMORY_AREA MemoryArea
= (PMEMORY_AREA
)Vad
;
1709 PROS_SECTION_OBJECT Section
;
1711 /* Check if it's a section view (RosMm section) */
1712 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1714 /* Get the section pointer to the SECTION_OBJECT */
1715 Section
= MemoryArea
->Data
.SectionData
.Section
;
1716 FileObject
= Section
->FileObject
;
1720 ASSERT(MemoryArea
->Type
== MEMORY_AREA_CACHE
);
1721 DPRINT1("VAD is a cache section!\n");
1727 /* Make sure it's not a VM VAD */
1728 if (Vad
->u
.VadFlags
.PrivateMemory
== 1)
1730 DPRINT1("VAD is not a section\n");
1734 /* Get the control area */
1735 ControlArea
= Vad
->ControlArea
;
1736 if ((ControlArea
== NULL
) || !ControlArea
->u
.Flags
.Image
)
1738 DPRINT1("Address is not a section\n");
1742 /* Get the file object */
1743 FileObject
= ControlArea
->FilePointer
;
1746 /* Return the file object */
1752 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation
)
1754 PSECTION_OBJECT SectionObject
;
1756 /* Get the section object of this process*/
1757 SectionObject
= PsGetCurrentProcess()->SectionObject
;
1758 ASSERT(SectionObject
!= NULL
);
1759 ASSERT(MiIsRosSectionObject(SectionObject
) == TRUE
);
1761 /* Return the image information */
1762 *ImageInformation
= ((PROS_SECTION_OBJECT
)SectionObject
)->ImageSection
->ImageInformation
;
1767 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject
,
1768 OUT POBJECT_NAME_INFORMATION
*ModuleName
)
1770 POBJECT_NAME_INFORMATION ObjectNameInfo
;
1774 /* Allocate memory for our structure */
1775 ObjectNameInfo
= ExAllocatePoolWithTag(PagedPool
, 1024, TAG_MM
);
1776 if (!ObjectNameInfo
) return STATUS_NO_MEMORY
;
1778 /* Query the name */
1779 Status
= ObQueryNameString(FileObject
,
1783 if (!NT_SUCCESS(Status
))
1785 /* Failed, free memory */
1786 DPRINT1("Name query failed\n");
1787 ExFreePoolWithTag(ObjectNameInfo
, TAG_MM
);
1793 *ModuleName
= ObjectNameInfo
;
1794 return STATUS_SUCCESS
;
1799 MmGetFileNameForSection(IN PVOID Section
,
1800 OUT POBJECT_NAME_INFORMATION
*ModuleName
)
1802 PFILE_OBJECT FileObject
;
1804 /* Make sure it's an image section */
1805 if (MiIsRosSectionObject(Section
) == FALSE
)
1807 /* Check ARM3 Section flag */
1808 if (((PSECTION
)Section
)->u
.Flags
.Image
== 0)
1810 /* It's not, fail */
1811 DPRINT1("Not an image section\n");
1812 return STATUS_SECTION_NOT_IMAGE
;
1815 else if (!(((PROS_SECTION_OBJECT
)Section
)->AllocationAttributes
& SEC_IMAGE
))
1817 /* It's not, fail */
1818 DPRINT1("Not an image section\n");
1819 return STATUS_SECTION_NOT_IMAGE
;
1822 /* Get the file object */
1823 FileObject
= MmGetFileObjectForSection(Section
);
1824 return MmGetFileNameForFileObject(FileObject
, ModuleName
);
1829 MmGetFileNameForAddress(IN PVOID Address
,
1830 OUT PUNICODE_STRING ModuleName
)
1832 POBJECT_NAME_INFORMATION ModuleNameInformation
;
1836 PFILE_OBJECT FileObject
= NULL
;
1838 /* Lock address space */
1839 AddressSpace
= MmGetCurrentAddressSpace();
1840 MmLockAddressSpace(AddressSpace
);
1843 Vad
= MiLocateAddress(Address
);
1846 /* Fail, the address does not exist */
1847 DPRINT1("No VAD at address %p\n", Address
);
1848 MmUnlockAddressSpace(AddressSpace
);
1849 return STATUS_INVALID_ADDRESS
;
1852 /* Get the file object pointer for the VAD */
1853 FileObject
= MiGetFileObjectForVad(Vad
);
1854 if (FileObject
== NULL
)
1856 DPRINT1("Failed to get file object for Address %p\n", Address
);
1857 MmUnlockAddressSpace(AddressSpace
);
1858 return STATUS_SECTION_NOT_IMAGE
;
1861 /* Reference the file object */
1862 ObReferenceObject(FileObject
);
1864 /* Unlock address space */
1865 MmUnlockAddressSpace(AddressSpace
);
1867 /* Get the filename of the file object */
1868 Status
= MmGetFileNameForFileObject(FileObject
, &ModuleNameInformation
);
1870 /* Dereference the file object */
1871 ObDereferenceObject(FileObject
);
1873 /* Check if we were able to get the file object name */
1874 if (NT_SUCCESS(Status
))
1876 /* Init modulename */
1877 RtlCreateUnicodeString(ModuleName
, ModuleNameInformation
->Name
.Buffer
);
1879 /* Free temp taged buffer from MmGetFileNameForFileObject() */
1880 ExFreePoolWithTag(ModuleNameInformation
, TAG_MM
);
1881 DPRINT("Found ModuleName %S by address %p\n", ModuleName
->Buffer
, Address
);
1890 MiQueryMemorySectionName(IN HANDLE ProcessHandle
,
1891 IN PVOID BaseAddress
,
1892 OUT PVOID MemoryInformation
,
1893 IN SIZE_T MemoryInformationLength
,
1894 OUT PSIZE_T ReturnLength
)
1898 WCHAR ModuleFileNameBuffer
[MAX_PATH
] = {0};
1899 UNICODE_STRING ModuleFileName
;
1900 PMEMORY_SECTION_NAME SectionName
= NULL
;
1901 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1903 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1904 PROCESS_QUERY_INFORMATION
,
1910 if (!NT_SUCCESS(Status
))
1912 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status
);
1916 RtlInitEmptyUnicodeString(&ModuleFileName
, ModuleFileNameBuffer
, sizeof(ModuleFileNameBuffer
));
1917 Status
= MmGetFileNameForAddress(BaseAddress
, &ModuleFileName
);
1919 if (NT_SUCCESS(Status
))
1921 SectionName
= MemoryInformation
;
1922 if (PreviousMode
!= KernelMode
)
1926 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
1927 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
1928 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
1930 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
1933 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1935 Status
= _SEH2_GetExceptionCode();
1941 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
1942 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
1943 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
1945 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
1949 ObDereferenceObject(Process
);
1955 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
1956 IN PMMPTE PointerPte
,
1957 IN ULONG ProtectionMask
,
1959 IN BOOLEAN UpdateDirty
)
1961 MMPTE TempPte
, PreviousPte
;
1963 BOOLEAN RebuildPte
= FALSE
;
1966 // User for sanity checking later on
1968 PreviousPte
= *PointerPte
;
1971 // Build the PTE and acquire the PFN lock
1973 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1976 PreviousPte
.u
.Hard
.PageFrameNumber
);
1977 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1980 // We don't support I/O mappings in this path yet
1982 ASSERT(Pfn1
!= NULL
);
1983 ASSERT(Pfn1
->u3
.e1
.CacheAttribute
!= MiWriteCombined
);
1986 // Make sure new protection mask doesn't get in conflict and fix it if it does
1988 if (Pfn1
->u3
.e1
.CacheAttribute
== MiCached
)
1991 // This is a cached PFN
1993 if (ProtectionMask
& (MM_NOCACHE
| MM_NOACCESS
))
1996 ProtectionMask
&= ~(MM_NOCACHE
| MM_NOACCESS
);
1999 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
2002 // This is a non-cached PFN
2004 if ((ProtectionMask
& (MM_NOCACHE
| MM_NOACCESS
)) != MM_NOCACHE
)
2007 ProtectionMask
&= ~MM_NOACCESS
;
2008 ProtectionMask
|= MM_NOCACHE
;
2014 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
2017 PreviousPte
.u
.Hard
.PageFrameNumber
);
2021 // Write the new PTE, making sure we are only changing the bits
2023 MI_UPDATE_VALID_PTE(PointerPte
, TempPte
);
2028 ASSERT(PreviousPte
.u
.Hard
.Valid
== 1);
2030 ASSERT(PreviousPte
.u
.Hard
.Valid
== 1);
2033 // Windows updates the relevant PFN1 information, we currently don't.
2035 if (UpdateDirty
&& PreviousPte
.u
.Hard
.Dirty
)
2037 if (!Pfn1
->u3
.e1
.Modified
)
2039 DPRINT1("FIXME: Mark PFN as dirty\n");
2044 // Not supported in ARM3
2046 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadWriteWatch
);
2049 // Release the PFN lock, we are done
2051 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2055 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
2059 MiSetProtectionOnSection(IN PEPROCESS Process
,
2061 IN PVOID StartingAddress
,
2062 IN PVOID EndingAddress
,
2063 IN ULONG NewProtect
,
2064 OUT PULONG CapturedOldProtect
,
2065 IN ULONG DontCharge
,
2068 PMMPTE PointerPte
, LastPte
;
2069 MMPTE TempPte
, PteContents
;
2072 ULONG ProtectionMask
, QuotaCharge
= 0;
2073 PETHREAD Thread
= PsGetCurrentThread();
2077 // Tell caller nothing is being locked
2082 // This function should only be used for section VADs. Windows ASSERT */
2084 ASSERT(FoundVad
->u
.VadFlags
.PrivateMemory
== 0);
2087 // We don't support these features in ARM3
2089 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadImageMap
);
2090 ASSERT(FoundVad
->u2
.VadFlags2
.CopyOnWrite
== 0);
2093 // Convert and validate the protection mask
2095 ProtectionMask
= MiMakeProtectionMask(NewProtect
);
2096 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2098 DPRINT1("Invalid section protect\n");
2099 return STATUS_INVALID_PAGE_PROTECTION
;
2103 // Get the PTE and PDE for the address, as well as the final PTE
2105 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
2106 PointerPde
= MiAddressToPde(StartingAddress
);
2107 PointerPte
= MiAddressToPte(StartingAddress
);
2108 LastPte
= MiAddressToPte(EndingAddress
);
2111 // Make the PDE valid, and check the status of the first PTE
2113 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2114 if (PointerPte
->u
.Long
)
2117 // Not supported in ARM3
2119 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
2122 // Capture the page protection and make the PDE valid
2124 *CapturedOldProtect
= MiGetPageProtection(PointerPte
);
2125 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2130 // Only pagefile-backed section VADs are supported for now
2132 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadImageMap
);
2135 // Grab the old protection from the VAD itself
2137 *CapturedOldProtect
= MmProtectToValue
[FoundVad
->u
.VadFlags
.Protection
];
2141 // Loop all the PTEs now
2143 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2144 while (PointerPte
<= LastPte
)
2147 // Check if we've crossed a PDE boundary and make the new PDE valid too
2149 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
2151 PointerPde
= MiPteToPde(PointerPte
);
2152 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
2156 // Capture the PTE and see what we're dealing with
2158 PteContents
= *PointerPte
;
2159 if (PteContents
.u
.Long
== 0)
2162 // This used to be a zero PTE and it no longer is, so we must add a
2163 // reference to the pagetable.
2165 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
2168 // Create the demand-zero prototype PTE
2170 TempPte
= PrototypePte
;
2171 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
2172 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
2174 else if (PteContents
.u
.Hard
.Valid
== 1)
2177 // Get the PFN entry
2179 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2182 // We don't support these yet
2184 ASSERT((NewProtect
& (PAGE_NOACCESS
| PAGE_GUARD
)) == 0);
2185 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
2188 // Write the protection mask and write it with a TLB flush
2190 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
2191 MiFlushTbAndCapture(FoundVad
,
2200 // We don't support these cases yet
2202 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2203 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
2206 // The PTE is already demand-zero, just update the protection mask
2208 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
2215 // Unlock the working set and update quota charges if needed, then return
2217 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
2218 if ((QuotaCharge
> 0) && (!DontCharge
))
2220 FoundVad
->u
.VadFlags
.CommitCharge
-= QuotaCharge
;
2221 Process
->CommitCharge
-= QuotaCharge
;
2223 return STATUS_SUCCESS
;
2228 MiRemoveMappedPtes(IN PVOID BaseAddress
,
2229 IN ULONG NumberOfPtes
,
2230 IN PCONTROL_AREA ControlArea
,
2233 PMMPTE PointerPte
, ProtoPte
;//, FirstPte;
2234 PMMPDE PointerPde
, SystemMapPde
;
2238 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress
);
2242 /* Get the PTE and loop each one */
2243 PointerPte
= MiAddressToPte(BaseAddress
);
2244 //FirstPte = PointerPte;
2245 while (NumberOfPtes
)
2247 /* Check if the PTE is already valid */
2248 PteContents
= *PointerPte
;
2249 if (PteContents
.u
.Hard
.Valid
== 1)
2251 /* Get the PFN entry */
2252 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2255 PointerPde
= MiPteToPde(PointerPte
);
2257 /* Lock the PFN database and make sure this isn't a mapped file */
2258 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2259 ASSERT(((Pfn1
->u3
.e1
.PrototypePte
) && (Pfn1
->OriginalPte
.u
.Soft
.Prototype
)) == 0);
2261 /* Mark the page as modified accordingly */
2262 if (MI_IS_PAGE_DIRTY(&PteContents
))
2263 Pfn1
->u3
.e1
.Modified
= 1;
2265 /* Was the PDE invalid */
2266 if (PointerPde
->u
.Long
== 0)
2268 #if (_MI_PAGING_LEVELS == 2)
2269 /* Find the system double-mapped PDE that describes this mapping */
2270 SystemMapPde
= &MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)];
2273 ASSERT(SystemMapPde
->u
.Hard
.Valid
== 1);
2274 MI_WRITE_VALID_PDE(PointerPde
, *SystemMapPde
);
2276 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde
);
2281 /* Dereference the PDE and the PTE */
2282 Pfn2
= MiGetPfnEntry(PFN_FROM_PTE(PointerPde
));
2283 MiDecrementShareCount(Pfn2
, PFN_FROM_PTE(PointerPde
));
2284 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2
);
2285 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2287 /* Release the PFN lock */
2288 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2292 /* Windows ASSERT */
2293 ASSERT((PteContents
.u
.Long
== 0) || (PteContents
.u
.Soft
.Prototype
== 1));
2295 /* Check if this is a prototype pointer PTE */
2296 if (PteContents
.u
.Soft
.Prototype
== 1)
2298 /* Get the prototype PTE */
2299 ProtoPte
= MiProtoPteToPte(&PteContents
);
2301 /* We don't support anything else atm */
2302 ASSERT(ProtoPte
->u
.Long
== 0);
2306 /* Make the PTE into a zero PTE */
2307 PointerPte
->u
.Long
= 0;
2309 /* Move to the next PTE */
2317 /* Acquire the PFN lock */
2318 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2320 /* Decrement the accounting counters */
2321 ControlArea
->NumberOfUserReferences
--;
2322 ControlArea
->NumberOfMappedViews
--;
2324 /* Check if we should destroy the CA and release the lock */
2325 MiCheckControlArea(ControlArea
, OldIrql
);
2330 MiRemoveFromSystemSpace(IN PMMSESSION Session
,
2332 OUT PCONTROL_AREA
*ControlArea
)
2334 ULONG Hash
, Size
, Count
= 0;
2338 /* Compute the hash for this entry and loop trying to find it */
2339 Entry
= (ULONG_PTR
)Base
>> 16;
2340 Hash
= Entry
% Session
->SystemSpaceHashKey
;
2341 while ((Session
->SystemSpaceViewTable
[Hash
].Entry
>> 16) != Entry
)
2343 /* Check if we overflew past the end of the hash table */
2344 if (++Hash
>= Session
->SystemSpaceHashSize
)
2346 /* Reset the hash to zero and keep searching from the bottom */
2350 /* But if we overflew twice, then this is not a real mapping */
2351 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW
,
2360 /* One less entry */
2361 Session
->SystemSpaceHashEntries
--;
2363 /* Extract the size and clear the entry */
2364 Size
= Session
->SystemSpaceViewTable
[Hash
].Entry
& 0xFFFF;
2365 Session
->SystemSpaceViewTable
[Hash
].Entry
= 0;
2367 /* Return the control area and the size */
2368 *ControlArea
= Session
->SystemSpaceViewTable
[Hash
].ControlArea
;
2374 MiUnmapViewInSystemSpace(IN PMMSESSION Session
,
2375 IN PVOID MappedBase
)
2378 PCONTROL_AREA ControlArea
;
2381 /* Remove this mapping */
2382 KeAcquireGuardedMutex(Session
->SystemSpaceViewLockPointer
);
2383 Size
= MiRemoveFromSystemSpace(Session
, MappedBase
, &ControlArea
);
2385 /* Clear the bits for this mapping */
2386 RtlClearBits(Session
->SystemSpaceBitMap
,
2387 (ULONG
)(((ULONG_PTR
)MappedBase
- (ULONG_PTR
)Session
->SystemSpaceViewStart
) >> 16),
2390 /* Convert the size from a bit size into the actual size */
2391 Size
= Size
* (_64K
>> PAGE_SHIFT
);
2393 /* Remove the PTEs now */
2394 MiRemoveMappedPtes(MappedBase
, Size
, ControlArea
, NULL
);
2395 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
2397 /* Return success */
2398 return STATUS_SUCCESS
;
2401 /* PUBLIC FUNCTIONS ***********************************************************/
2408 MmCreateArm3Section(OUT PVOID
*SectionObject
,
2409 IN ACCESS_MASK DesiredAccess
,
2410 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
2411 IN PLARGE_INTEGER InputMaximumSize
,
2412 IN ULONG SectionPageProtection
,
2413 IN ULONG AllocationAttributes
,
2414 IN HANDLE FileHandle OPTIONAL
,
2415 IN PFILE_OBJECT FileObject OPTIONAL
)
2418 PSECTION NewSection
;
2419 PSUBSECTION Subsection
;
2420 PSEGMENT NewSegment
, Segment
;
2422 PCONTROL_AREA ControlArea
;
2423 ULONG ProtectionMask
, ControlAreaSize
, Size
, NonPagedCharge
, PagedCharge
;
2424 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2425 BOOLEAN FileLock
= FALSE
, KernelCall
= FALSE
;
2428 BOOLEAN UserRefIncremented
= FALSE
;
2429 PVOID PreviousSectionPointer
;
2431 /* Make the same sanity checks that the Nt interface should've validated */
2432 ASSERT((AllocationAttributes
& ~(SEC_COMMIT
| SEC_RESERVE
| SEC_BASED
|
2433 SEC_LARGE_PAGES
| SEC_IMAGE
| SEC_NOCACHE
|
2434 SEC_NO_CHANGE
)) == 0);
2435 ASSERT((AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
| SEC_IMAGE
)) != 0);
2436 ASSERT(!((AllocationAttributes
& SEC_IMAGE
) &&
2437 (AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
|
2438 SEC_NOCACHE
| SEC_NO_CHANGE
))));
2439 ASSERT(!((AllocationAttributes
& SEC_COMMIT
) && (AllocationAttributes
& SEC_RESERVE
)));
2440 ASSERT(!((SectionPageProtection
& PAGE_NOCACHE
) ||
2441 (SectionPageProtection
& PAGE_WRITECOMBINE
) ||
2442 (SectionPageProtection
& PAGE_GUARD
) ||
2443 (SectionPageProtection
& PAGE_NOACCESS
)));
2445 /* Convert section flag to page flag */
2446 if (AllocationAttributes
& SEC_NOCACHE
) SectionPageProtection
|= PAGE_NOCACHE
;
2448 /* Check to make sure the protection is correct. Nt* does this already */
2449 ProtectionMask
= MiMakeProtectionMask(SectionPageProtection
);
2450 if (ProtectionMask
== MM_INVALID_PROTECTION
) return STATUS_INVALID_PAGE_PROTECTION
;
2452 /* Check if this is going to be a data or image backed file section */
2453 if ((FileHandle
) || (FileObject
))
2455 /* These cannot be mapped with large pages */
2456 if (AllocationAttributes
& SEC_LARGE_PAGES
) return STATUS_INVALID_PARAMETER_6
;
2458 /* For now, only support the mechanism through a file handle */
2459 ASSERT(FileObject
== NULL
);
2461 /* Reference the file handle to get the object */
2462 Status
= ObReferenceObjectByHandle(FileHandle
,
2463 MmMakeFileAccess
[ProtectionMask
],
2468 if (!NT_SUCCESS(Status
)) return Status
;
2470 /* Make sure Cc has been doing its job */
2471 if (!File
->SectionObjectPointer
)
2473 /* This is not a valid file system-based file, fail */
2474 ObDereferenceObject(File
);
2475 return STATUS_INVALID_FILE_FOR_SECTION
;
2478 /* Image-file backed sections are not yet supported */
2479 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2481 /* Compute the size of the control area, and allocate it */
2482 ControlAreaSize
= sizeof(CONTROL_AREA
) + sizeof(MSUBSECTION
);
2483 ControlArea
= ExAllocatePoolWithTag(NonPagedPool
, ControlAreaSize
, 'aCmM');
2486 ObDereferenceObject(File
);
2487 return STATUS_INSUFFICIENT_RESOURCES
;
2491 RtlZeroMemory(ControlArea
, ControlAreaSize
);
2493 /* Did we get a handle, or an object? */
2496 /* We got a file handle so we have to lock down the file */
2498 Status
= FsRtlAcquireToCreateMappedSection(File
, SectionPageProtection
);
2499 if (!NT_SUCCESS(Status
))
2501 ExFreePool(ControlArea
);
2502 ObDereferenceObject(File
);
2506 /* ReactOS doesn't support this API yet, so do nothing */
2507 Status
= STATUS_SUCCESS
;
2509 /* Update the top-level IRP so that drivers know what's happening */
2510 IoSetTopLevelIrp((PIRP
)FSRTL_FSP_TOP_LEVEL_IRP
);
2514 /* Lock the PFN database while we play with the section pointers */
2515 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2517 /* Image-file backed sections are not yet supported */
2518 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2520 /* There should not already be a control area for this file */
2521 ASSERT(File
->SectionObjectPointer
->DataSectionObject
== NULL
);
2524 /* Write down that this CA is being created, and set it */
2525 ControlArea
->u
.Flags
.BeingCreated
= TRUE
;
2526 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2527 PreviousSectionPointer
= File
->SectionObjectPointer
;
2528 File
->SectionObjectPointer
->DataSectionObject
= ControlArea
;
2530 /* We can release the PFN lock now */
2531 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2533 /* We don't support previously-mapped file */
2534 ASSERT(NewSegment
== NULL
);
2536 /* Image-file backed sections are not yet supported */
2537 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2539 /* So we always create a data file map */
2540 Status
= MiCreateDataFileMap(File
,
2542 (PSIZE_T
)InputMaximumSize
,
2543 SectionPageProtection
,
2544 AllocationAttributes
,
2546 if (!NT_SUCCESS(Status
))
2548 /* Lock the PFN database while we play with the section pointers */
2549 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2551 /* Reset the waiting-for-deletion event */
2552 ASSERT(ControlArea
->WaitingForDeletion
== NULL
);
2553 ControlArea
->WaitingForDeletion
= NULL
;
2555 /* Set the file pointer NULL flag */
2556 ASSERT(ControlArea
->u
.Flags
.FilePointerNull
== 0);
2557 ControlArea
->u
.Flags
.FilePointerNull
= TRUE
;
2559 /* Delete the data section object */
2560 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2561 File
->SectionObjectPointer
->DataSectionObject
= NULL
;
2563 /* No longer being created */
2564 ControlArea
->u
.Flags
.BeingCreated
= FALSE
;
2566 /* We can release the PFN lock now */
2567 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2569 /* Check if we locked and set the IRP */
2573 IoSetTopLevelIrp(NULL
);
2574 //FsRtlReleaseFile(File);
2577 /* Free the control area and de-ref the file object */
2578 ExFreePool(ControlArea
);
2579 ObDereferenceObject(File
);
2585 /* On success, we expect this */
2586 ASSERT(PreviousSectionPointer
== File
->SectionObjectPointer
);
2588 /* Check if a maximum size was specified */
2589 if (!InputMaximumSize
->QuadPart
)
2591 /* Nope, use the segment size */
2592 Section
.SizeOfSection
.QuadPart
= (LONGLONG
)Segment
->SizeOfSegment
;
2596 /* Yep, use the entered size */
2597 Section
.SizeOfSection
.QuadPart
= InputMaximumSize
->QuadPart
;
2602 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2603 if (AllocationAttributes
& SEC_IMAGE
) return STATUS_INVALID_FILE_FOR_SECTION
;
2605 /* Not yet supported */
2606 ASSERT((AllocationAttributes
& SEC_LARGE_PAGES
) == 0);
2608 /* So this must be a pagefile-backed section, create the mappings needed */
2609 Status
= MiCreatePagingFileMap(&NewSegment
,
2610 (PSIZE_T
)InputMaximumSize
,
2612 AllocationAttributes
);
2613 if (!NT_SUCCESS(Status
)) return Status
;
2615 /* Set the size here, and read the control area */
2616 Section
.SizeOfSection
.QuadPart
= NewSegment
->SizeOfSegment
;
2617 ControlArea
= NewSegment
->ControlArea
;
2619 /* MiCreatePagingFileMap increments user references */
2620 UserRefIncremented
= TRUE
;
2623 /* Did we already have a segment? */
2626 /* This must be the file path and we created a segment */
2627 NewSegment
= Segment
;
2628 ASSERT(File
!= NULL
);
2630 /* Acquire the PFN lock while we set control area flags */
2631 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2633 /* We don't support this race condition yet, so assume no waiters */
2634 ASSERT(ControlArea
->WaitingForDeletion
== NULL
);
2635 ControlArea
->WaitingForDeletion
= NULL
;
2637 /* Image-file backed sections are not yet supported, nor ROM images */
2638 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2639 ASSERT(Segment
->ControlArea
->u
.Flags
.Rom
== 0);
2641 /* Take off the being created flag, and then release the lock */
2642 ControlArea
->u
.Flags
.BeingCreated
= FALSE
;
2643 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2646 /* Check if we locked the file earlier */
2649 /* Reset the top-level IRP and release the lock */
2650 IoSetTopLevelIrp(NULL
);
2651 //FsRtlReleaseFile(File);
2655 /* Set the initial section object data */
2656 Section
.InitialPageProtection
= SectionPageProtection
;
2658 /* The mapping created a control area and segment, save the flags */
2659 Section
.Segment
= NewSegment
;
2660 Section
.u
.LongFlags
= ControlArea
->u
.LongFlags
;
2662 /* Check if this is a user-mode read-write non-image file mapping */
2663 if (!(FileObject
) &&
2664 (SectionPageProtection
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)) &&
2665 !(ControlArea
->u
.Flags
.Image
) &&
2666 (ControlArea
->FilePointer
))
2668 /* Add a reference and set the flag */
2669 Section
.u
.Flags
.UserWritable
= TRUE
;
2670 InterlockedIncrement((volatile LONG
*)&ControlArea
->WritableUserReferences
);
2673 /* Check for image mappings or page file mappings */
2674 if ((ControlArea
->u
.Flags
.Image
) || !(ControlArea
->FilePointer
))
2676 /* Charge the segment size, and allocate a subsection */
2677 PagedCharge
= sizeof(SECTION
) + NewSegment
->TotalNumberOfPtes
* sizeof(MMPTE
);
2678 Size
= sizeof(SUBSECTION
);
2682 /* Charge nothing, and allocate a mapped subsection */
2684 Size
= sizeof(MSUBSECTION
);
2687 /* Check if this is a normal CA */
2688 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
2689 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
2691 /* Charge only a CA, and the subsection is right after */
2692 NonPagedCharge
= sizeof(CONTROL_AREA
);
2693 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
2695 /* We only support single-subsection mappings */
2696 NonPagedCharge
+= Size
;
2697 ASSERT(Subsection
->NextSubsection
== NULL
);
2699 /* Create the actual section object, with enough space for the prototype PTEs */
2700 Status
= ObCreateObject(PreviousMode
,
2701 MmSectionObjectType
,
2708 (PVOID
*)&NewSection
);
2709 if (!NT_SUCCESS(Status
))
2711 /* Check if this is a user-mode read-write non-image file mapping */
2712 if (!(FileObject
) &&
2713 (SectionPageProtection
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)) &&
2714 !(ControlArea
->u
.Flags
.Image
) &&
2715 (ControlArea
->FilePointer
))
2717 /* Remove a reference and check the flag */
2718 ASSERT(Section
.u
.Flags
.UserWritable
== 1);
2719 InterlockedDecrement((volatile LONG
*)&ControlArea
->WritableUserReferences
);
2722 /* Check if a user reference was added */
2723 if (UserRefIncremented
)
2725 /* Acquire the PFN lock while we change counters */
2726 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2728 /* Decrement the accounting counters */
2729 ControlArea
->NumberOfSectionReferences
--;
2730 ASSERT((LONG
)ControlArea
->NumberOfUserReferences
> 0);
2731 ControlArea
->NumberOfUserReferences
--;
2733 /* Check if we should destroy the CA and release the lock */
2734 MiCheckControlArea(ControlArea
, OldIrql
);
2737 /* Return the failure code */
2741 /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */
2743 /* Now copy the local section object from the stack into this new object */
2744 RtlCopyMemory(NewSection
, &Section
, sizeof(SECTION
));
2745 NewSection
->Address
.StartingVpn
= 0;
2747 /* For now, only user calls are supported */
2748 ASSERT(KernelCall
== FALSE
);
2749 NewSection
->u
.Flags
.UserReference
= TRUE
;
2751 /* Is this a "based" allocation, in which all mappings are identical? */
2752 if (AllocationAttributes
& SEC_BASED
)
2754 /* Lock the VAD tree during the search */
2755 KeAcquireGuardedMutex(&MmSectionBasedMutex
);
2757 /* Is it a brand new ControArea ? */
2758 if (ControlArea
->u
.Flags
.BeingCreated
== 1)
2760 ASSERT(ControlArea
->u
.Flags
.Based
== 1);
2761 /* Then we must find a global address, top-down */
2762 Status
= MiFindEmptyAddressRangeDownBasedTree((SIZE_T
)ControlArea
->Segment
->SizeOfSegment
,
2763 (ULONG_PTR
)MmHighSectionBase
,
2765 &MmSectionBasedRoot
,
2766 (ULONG_PTR
*)&ControlArea
->Segment
->BasedAddress
);
2768 if (!NT_SUCCESS(Status
))
2770 /* No way to find a valid range. */
2771 KeReleaseGuardedMutex(&MmSectionBasedMutex
);
2772 ControlArea
->u
.Flags
.Based
= 0;
2773 NewSection
->u
.Flags
.Based
= 0;
2774 ObDereferenceObject(NewSection
);
2778 /* Compute the ending address and insert it into the VAD tree */
2779 NewSection
->Address
.StartingVpn
= (ULONG_PTR
)ControlArea
->Segment
->BasedAddress
;
2780 NewSection
->Address
.EndingVpn
= NewSection
->Address
.StartingVpn
+ NewSection
->SizeOfSection
.LowPart
- 1;
2781 MiInsertBasedSection(NewSection
);
2785 /* FIXME : Should we deny section creation if SEC_BASED is not set ? Can we have two different section objects on the same based address ? Investigate !*/
2789 KeReleaseGuardedMutex(&MmSectionBasedMutex
);
2792 /* The control area is not being created anymore */
2793 if (ControlArea
->u
.Flags
.BeingCreated
== 1)
2795 /* Acquire the PFN lock while we set control area flags */
2796 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2798 /* Take off the being created flag, and then release the lock */
2799 ControlArea
->u
.Flags
.BeingCreated
= 0;
2800 NewSection
->u
.Flags
.BeingCreated
= 0;
2802 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2805 /* Migrate the attribute into a flag */
2806 if (AllocationAttributes
& SEC_NO_CHANGE
) NewSection
->u
.Flags
.NoChange
= TRUE
;
2808 /* If R/W access is not requested, this might eventually become a CoW mapping */
2809 if (!(SectionPageProtection
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
2811 NewSection
->u
.Flags
.CopyOnWrite
= TRUE
;
2814 /* Write down if this was a kernel call */
2815 ControlArea
->u
.Flags
.WasPurged
|= KernelCall
;
2816 ASSERT(ControlArea
->u
.Flags
.WasPurged
== FALSE
);
2818 /* Make sure the segment and the section are the same size, or the section is smaller */
2819 ASSERT((ULONG64
)NewSection
->SizeOfSection
.QuadPart
<= NewSection
->Segment
->SizeOfSegment
);
2821 /* Return the object and the creation status */
2822 *SectionObject
= (PVOID
)NewSection
;
2831 MmMapViewOfArm3Section(IN PVOID SectionObject
,
2832 IN PEPROCESS Process
,
2833 IN OUT PVOID
*BaseAddress
,
2834 IN ULONG_PTR ZeroBits
,
2835 IN SIZE_T CommitSize
,
2836 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL
,
2837 IN OUT PSIZE_T ViewSize
,
2838 IN SECTION_INHERIT InheritDisposition
,
2839 IN ULONG AllocationType
,
2842 KAPC_STATE ApcState
;
2843 BOOLEAN Attached
= FALSE
;
2845 PCONTROL_AREA ControlArea
;
2846 ULONG ProtectionMask
;
2850 /* Get the segment and control area */
2851 Section
= (PSECTION
)SectionObject
;
2852 ControlArea
= Section
->Segment
->ControlArea
;
2854 /* These flags/states are not yet supported by ARM3 */
2855 ASSERT(Section
->u
.Flags
.Image
== 0);
2856 ASSERT(Section
->u
.Flags
.NoCache
== 0);
2857 ASSERT(Section
->u
.Flags
.WriteCombined
== 0);
2858 ASSERT(ControlArea
->u
.Flags
.PhysicalMemory
== 0);
2861 if ((AllocationType
& MEM_RESERVE
) != 0)
2863 DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2864 return STATUS_NOT_IMPLEMENTED
;
2867 /* Check if the mapping protection is compatible with the create */
2868 if (!MiIsProtectionCompatible(Section
->InitialPageProtection
, Protect
))
2870 DPRINT1("Mapping protection is incompatible\n");
2871 return STATUS_SECTION_PROTECTION
;
2874 /* Check if the offset and size would cause an overflow */
2875 if (((ULONG64
)SectionOffset
->QuadPart
+ *ViewSize
) <
2876 (ULONG64
)SectionOffset
->QuadPart
)
2878 DPRINT1("Section offset overflows\n");
2879 return STATUS_INVALID_VIEW_SIZE
;
2882 /* Check if the offset and size are bigger than the section itself */
2883 if (((ULONG64
)SectionOffset
->QuadPart
+ *ViewSize
) >
2884 (ULONG64
)Section
->SizeOfSection
.QuadPart
)
2886 DPRINT1("Section offset is larger than section\n");
2887 return STATUS_INVALID_VIEW_SIZE
;
2890 /* Check if the caller did not specify a view size */
2893 /* Compute it for the caller */
2894 *ViewSize
= (SIZE_T
)(Section
->SizeOfSection
.QuadPart
- SectionOffset
->QuadPart
);
2896 /* Check if it's larger than 4GB or overflows into kernel-mode */
2897 if ((*ViewSize
> 0xFFFFFFFF) ||
2898 (((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
- (ULONG_PTR
)*BaseAddress
) < *ViewSize
))
2900 DPRINT1("Section view won't fit\n");
2901 return STATUS_INVALID_VIEW_SIZE
;
2905 /* Check if the commit size is larger than the view size */
2906 if (CommitSize
> *ViewSize
)
2908 DPRINT1("Attempting to commit more than the view itself\n");
2909 return STATUS_INVALID_PARAMETER_5
;
2912 /* Check if the view size is larger than the section */
2913 if (*ViewSize
> (ULONG64
)Section
->SizeOfSection
.QuadPart
)
2915 DPRINT1("The view is larger than the section\n");
2916 return STATUS_INVALID_VIEW_SIZE
;
2919 /* Compute and validate the protection mask */
2920 ProtectionMask
= MiMakeProtectionMask(Protect
);
2921 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2923 DPRINT1("The protection is invalid\n");
2924 return STATUS_INVALID_PAGE_PROTECTION
;
2927 /* We only handle pagefile-backed sections, which cannot be writecombined */
2928 if (Protect
& PAGE_WRITECOMBINE
)
2930 DPRINT1("Cannot write combine a pagefile-backed section\n");
2931 return STATUS_INVALID_PARAMETER_10
;
2934 /* Start by attaching to the current process if needed */
2935 if (PsGetCurrentProcess() != Process
)
2937 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2941 /* Do the actual mapping */
2942 Status
= MiMapViewOfDataSection(ControlArea
,
2954 /* Detach if needed, then return status */
2955 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2964 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
)
2975 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
,
2976 IN BOOLEAN DelayClose
)
2987 MmMapViewInSessionSpace(IN PVOID Section
,
2988 OUT PVOID
*MappedBase
,
2989 IN OUT PSIZE_T ViewSize
)
2994 if (MiIsRosSectionObject(Section
))
2996 return MmMapViewInSystemSpace(Section
, MappedBase
, ViewSize
);
2999 /* Process must be in a session */
3000 if (PsGetCurrentProcess()->ProcessInSession
== FALSE
)
3002 DPRINT1("Process is not in session\n");
3003 return STATUS_NOT_MAPPED_VIEW
;
3006 /* Use the system space API, but with the session view instead */
3007 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
3008 return MiMapViewInSystemSpace(Section
,
3009 &MmSessionSpace
->Session
,
3019 MmUnmapViewInSessionSpace(IN PVOID MappedBase
)
3024 if (!MI_IS_SESSION_ADDRESS(MappedBase
))
3026 return MmUnmapViewInSystemSpace(MappedBase
);
3029 /* Process must be in a session */
3030 if (PsGetCurrentProcess()->ProcessInSession
== FALSE
)
3032 DPRINT1("Proess is not in session\n");
3033 return STATUS_NOT_MAPPED_VIEW
;
3036 /* Use the system space API, but with the session view instead */
3037 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
3038 return MiUnmapViewInSystemSpace(&MmSessionSpace
->Session
,
3047 MmUnmapViewOfSection(IN PEPROCESS Process
,
3048 IN PVOID BaseAddress
)
3050 return MiUnmapViewOfSection(Process
, BaseAddress
, 0);
3058 MmUnmapViewInSystemSpace(IN PVOID MappedBase
)
3060 PMEMORY_AREA MemoryArea
;
3063 /* Was this mapped by RosMm? */
3064 MemoryArea
= MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase
);
3065 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
3067 return MiRosUnmapViewInSystemSpace(MappedBase
);
3070 /* It was not, call the ARM3 routine */
3071 return MiUnmapViewInSystemSpace(&MmSession
, MappedBase
);
3079 MmCommitSessionMappedView(IN PVOID MappedBase
,
3082 ULONG_PTR StartAddress
, EndingAddress
, Base
;
3083 ULONG Hash
, Count
= 0, Size
, QuotaCharge
;
3085 PMMPTE LastProtoPte
, PointerPte
, ProtoPte
;
3086 PCONTROL_AREA ControlArea
;
3088 PSUBSECTION Subsection
;
3092 /* Make sure the base isn't past the session view range */
3093 if ((MappedBase
< MiSessionViewStart
) ||
3094 (MappedBase
>= (PVOID
)((ULONG_PTR
)MiSessionViewStart
+ MmSessionViewSize
)))
3096 DPRINT1("Base outside of valid range\n");
3097 return STATUS_INVALID_PARAMETER_1
;
3100 /* Make sure the size isn't past the session view range */
3101 if (((ULONG_PTR
)MiSessionViewStart
+ MmSessionViewSize
-
3102 (ULONG_PTR
)MappedBase
) < ViewSize
)
3104 DPRINT1("Size outside of valid range\n");
3105 return STATUS_INVALID_PARAMETER_2
;
3109 ASSERT(ViewSize
!= 0);
3111 /* Process must be in a session */
3112 if (PsGetCurrentProcess()->ProcessInSession
== FALSE
)
3114 DPRINT1("Process is not in session\n");
3115 return STATUS_NOT_MAPPED_VIEW
;
3118 /* Compute the correctly aligned base and end addresses */
3119 StartAddress
= (ULONG_PTR
)PAGE_ALIGN(MappedBase
);
3120 EndingAddress
= ((ULONG_PTR
)MappedBase
+ ViewSize
- 1) | (PAGE_SIZE
- 1);
3122 /* Sanity check and grab the session */
3123 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
3124 Session
= &MmSessionSpace
->Session
;
3126 /* Get the hash entry for this allocation */
3127 Hash
= (StartAddress
>> 16) % Session
->SystemSpaceHashKey
;
3129 /* Lock system space */
3130 KeAcquireGuardedMutex(Session
->SystemSpaceViewLockPointer
);
3132 /* Loop twice so we can try rolling over if needed */
3135 /* Extract the size and base addresses from the entry */
3136 Base
= Session
->SystemSpaceViewTable
[Hash
].Entry
& ~0xFFFF;
3137 Size
= Session
->SystemSpaceViewTable
[Hash
].Entry
& 0xFFFF;
3139 /* Convert the size to bucket chunks */
3140 Size
*= MI_SYSTEM_VIEW_BUCKET_SIZE
;
3142 /* Bail out if this entry fits in here */
3143 if ((StartAddress
>= Base
) && (EndingAddress
< (Base
+ Size
))) break;
3145 /* Check if we overflew past the end of the hash table */
3146 if (++Hash
>= Session
->SystemSpaceHashSize
)
3148 /* Reset the hash to zero and keep searching from the bottom */
3152 /* But if we overflew twice, then this is not a real mapping */
3153 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW
,
3162 /* Make sure the view being mapped is not file-based */
3163 ControlArea
= Session
->SystemSpaceViewTable
[Hash
].ControlArea
;
3164 if (ControlArea
->FilePointer
!= NULL
)
3166 /* It is, so we have to bail out */
3167 DPRINT1("Only page-filed backed sections can be commited\n");
3168 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
3169 return STATUS_ALREADY_COMMITTED
;
3172 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
3173 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
3174 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
3175 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
3177 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
3178 ProtoPte
= Subsection
->SubsectionBase
+ ((StartAddress
- Base
) >> PAGE_SHIFT
);
3179 QuotaCharge
= MiAddressToPte(EndingAddress
) - MiAddressToPte(StartAddress
) + 1;
3180 LastProtoPte
= ProtoPte
+ QuotaCharge
;
3181 if (LastProtoPte
>= Subsection
->SubsectionBase
+ Subsection
->PtesInSubsection
)
3183 DPRINT1("PTE is out of bounds\n");
3184 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
3185 return STATUS_INVALID_PARAMETER_2
;
3188 /* Acquire the commit lock and count all the non-committed PTEs */
3189 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
3190 PointerPte
= ProtoPte
;
3191 while (PointerPte
< LastProtoPte
)
3193 if (PointerPte
->u
.Long
) QuotaCharge
--;
3197 /* Was everything committed already? */
3200 /* Nothing to do! */
3201 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
3202 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
3203 return STATUS_SUCCESS
;
3206 /* Pick the segment and template PTE */
3207 Segment
= ControlArea
->Segment
;
3208 TempPte
= Segment
->SegmentPteTemplate
;
3209 ASSERT(TempPte
.u
.Long
!= 0);
3211 /* Loop all prototype PTEs to be committed */
3212 PointerPte
= ProtoPte
;
3213 while (PointerPte
< LastProtoPte
)
3215 /* Make sure the PTE is already invalid */
3216 if (PointerPte
->u
.Long
== 0)
3218 /* And write the invalid PTE */
3219 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
3222 /* Move to the next PTE */
3226 /* Check if we had at least one page charged */
3229 /* Update the accounting data */
3230 Segment
->NumberOfCommittedPages
+= QuotaCharge
;
3231 InterlockedExchangeAddSizeT(&MmSharedCommit
, QuotaCharge
);
3235 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
3236 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
3237 return STATUS_SUCCESS
;
3242 MiDeleteARM3Section(PVOID ObjectBody
)
3244 PSECTION SectionObject
;
3245 PCONTROL_AREA ControlArea
;
3248 SectionObject
= (PSECTION
)ObjectBody
;
3250 if (SectionObject
->u
.Flags
.Based
== 1)
3252 /* Remove the node from the global section address tree */
3253 KeAcquireGuardedMutex(&MmSectionBasedMutex
);
3254 MiRemoveNode(&SectionObject
->Address
, &MmSectionBasedRoot
);
3255 KeReleaseGuardedMutex(&MmSectionBasedMutex
);
3258 /* Lock the PFN database */
3259 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
3261 ASSERT(SectionObject
->Segment
);
3262 ASSERT(SectionObject
->Segment
->ControlArea
);
3264 ControlArea
= SectionObject
->Segment
->ControlArea
;
3267 ControlArea
->NumberOfSectionReferences
--;
3268 ControlArea
->NumberOfUserReferences
--;
3270 ASSERT(ControlArea
->u
.Flags
.BeingDeleted
== 0);
3272 /* Check it. It will delete it if there is no more reference to it */
3273 MiCheckControlArea(ControlArea
, OldIrql
);
3278 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer
)
3284 /* SYSTEM CALLS ***************************************************************/
3288 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage
,
3289 IN PVOID File2MappedAsFile
)
3293 PFILE_OBJECT FileObject1
, FileObject2
;
3296 /* Lock address space */
3297 AddressSpace
= MmGetCurrentAddressSpace();
3298 MmLockAddressSpace(AddressSpace
);
3300 /* Get the VAD for Address 1 */
3301 Vad1
= MiLocateAddress(File1MappedAsAnImage
);
3304 /* Fail, the address does not exist */
3305 DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage
);
3306 Status
= STATUS_INVALID_ADDRESS
;
3310 /* Get the VAD for Address 2 */
3311 Vad2
= MiLocateAddress(File2MappedAsFile
);
3314 /* Fail, the address does not exist */
3315 DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile
);
3316 Status
= STATUS_INVALID_ADDRESS
;
3320 /* Get the file object pointer for VAD 1 */
3321 FileObject1
= MiGetFileObjectForVad(Vad1
);
3322 if (FileObject1
== NULL
)
3324 DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage
);
3325 Status
= STATUS_CONFLICTING_ADDRESSES
;
3329 /* Get the file object pointer for VAD 2 */
3330 FileObject2
= MiGetFileObjectForVad(Vad2
);
3331 if (FileObject2
== NULL
)
3333 DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile
);
3334 Status
= STATUS_CONFLICTING_ADDRESSES
;
3338 /* Make sure Vad1 is an image mapping */
3339 if (Vad1
->u
.VadFlags
.VadType
!= VadImageMap
)
3341 DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage
);
3342 Status
= STATUS_NOT_SAME_DEVICE
;
3346 /* SectionObjectPointer is equal if the files are equal */
3347 if (FileObject1
->SectionObjectPointer
== FileObject2
->SectionObjectPointer
)
3349 Status
= STATUS_SUCCESS
;
3353 Status
= STATUS_NOT_SAME_DEVICE
;
3357 /* Unlock address space */
3358 MmUnlockAddressSpace(AddressSpace
);
3367 NtCreateSection(OUT PHANDLE SectionHandle
,
3368 IN ACCESS_MASK DesiredAccess
,
3369 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
3370 IN PLARGE_INTEGER MaximumSize OPTIONAL
,
3371 IN ULONG SectionPageProtection OPTIONAL
,
3372 IN ULONG AllocationAttributes
,
3373 IN HANDLE FileHandle OPTIONAL
)
3375 LARGE_INTEGER SafeMaximumSize
;
3376 PVOID SectionObject
;
3378 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3382 /* Check for non-existing flags */
3383 if ((AllocationAttributes
& ~(SEC_COMMIT
| SEC_RESERVE
| SEC_BASED
|
3384 SEC_LARGE_PAGES
| SEC_IMAGE
| SEC_NOCACHE
|
3387 if (!(AllocationAttributes
& 1))
3389 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes
);
3390 return STATUS_INVALID_PARAMETER_6
;
3394 /* Check for no allocation type */
3395 if (!(AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
| SEC_IMAGE
)))
3397 DPRINT1("Missing allocation type in allocation attributes\n");
3398 return STATUS_INVALID_PARAMETER_6
;
3401 /* Check for image allocation with invalid attributes */
3402 if ((AllocationAttributes
& SEC_IMAGE
) &&
3403 (AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
| SEC_LARGE_PAGES
|
3404 SEC_NOCACHE
| SEC_NO_CHANGE
)))
3406 DPRINT1("Image allocation with invalid attributes\n");
3407 return STATUS_INVALID_PARAMETER_6
;
3410 /* Check for allocation type is both commit and reserve */
3411 if ((AllocationAttributes
& SEC_COMMIT
) && (AllocationAttributes
& SEC_RESERVE
))
3413 DPRINT1("Commit and reserve in the same time\n");
3414 return STATUS_INVALID_PARAMETER_6
;
3417 /* Now check for valid protection */
3418 if ((SectionPageProtection
& PAGE_NOCACHE
) ||
3419 (SectionPageProtection
& PAGE_WRITECOMBINE
) ||
3420 (SectionPageProtection
& PAGE_GUARD
) ||
3421 (SectionPageProtection
& PAGE_NOACCESS
))
3423 DPRINT1("Sections don't support these protections\n");
3424 return STATUS_INVALID_PAGE_PROTECTION
;
3427 /* Use a maximum size of zero, if none was specified */
3428 SafeMaximumSize
.QuadPart
= 0;
3430 /* Check for user-mode caller */
3431 if (PreviousMode
!= KernelMode
)
3436 /* Safely check user-mode parameters */
3437 if (MaximumSize
) SafeMaximumSize
= ProbeForReadLargeInteger(MaximumSize
);
3438 MaximumSize
= &SafeMaximumSize
;
3439 ProbeForWriteHandle(SectionHandle
);
3441 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3443 /* Return the exception code */
3444 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3448 else if (!MaximumSize
) MaximumSize
= &SafeMaximumSize
;
3450 /* Check that MaximumSize is valid if backed by paging file */
3451 if ((!FileHandle
) && (!MaximumSize
->QuadPart
))
3452 return STATUS_INVALID_PARAMETER_4
;
3454 /* Create the section */
3455 Status
= MmCreateSection(&SectionObject
,
3459 SectionPageProtection
,
3460 AllocationAttributes
,
3463 if (!NT_SUCCESS(Status
)) return Status
;
3465 /* FIXME: Should zero last page for a file mapping */
3467 /* Now insert the object */
3468 Status
= ObInsertObject(SectionObject
,
3474 if (NT_SUCCESS(Status
))
3479 /* Return the handle safely */
3480 *SectionHandle
= Handle
;
3482 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3489 /* Return the status */
3495 NtOpenSection(OUT PHANDLE SectionHandle
,
3496 IN ACCESS_MASK DesiredAccess
,
3497 IN POBJECT_ATTRIBUTES ObjectAttributes
)
3501 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3504 /* Check for user-mode caller */
3505 if (PreviousMode
!= KernelMode
)
3510 /* Safely check user-mode parameters */
3511 ProbeForWriteHandle(SectionHandle
);
3513 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3515 /* Return the exception code */
3516 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3521 /* Try opening the object */
3522 Status
= ObOpenObjectByName(ObjectAttributes
,
3523 MmSectionObjectType
,
3533 /* Return the handle safely */
3534 *SectionHandle
= Handle
;
3536 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3542 /* Return the status */
3548 NtMapViewOfSection(IN HANDLE SectionHandle
,
3549 IN HANDLE ProcessHandle
,
3550 IN OUT PVOID
* BaseAddress
,
3551 IN ULONG_PTR ZeroBits
,
3552 IN SIZE_T CommitSize
,
3553 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL
,
3554 IN OUT PSIZE_T ViewSize
,
3555 IN SECTION_INHERIT InheritDisposition
,
3556 IN ULONG AllocationType
,
3559 PVOID SafeBaseAddress
;
3560 LARGE_INTEGER SafeSectionOffset
;
3561 SIZE_T SafeViewSize
;
3562 PROS_SECTION_OBJECT Section
;
3565 ACCESS_MASK DesiredAccess
;
3566 ULONG ProtectionMask
;
3567 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3569 static const ULONG ValidAllocationType
= (MEM_TOP_DOWN
| MEM_LARGE_PAGES
|
3570 MEM_DOS_LIM
| SEC_NO_CHANGE
| MEM_RESERVE
);
3572 static const ULONG ValidAllocationType
= (MEM_TOP_DOWN
| MEM_LARGE_PAGES
|
3573 SEC_NO_CHANGE
| MEM_RESERVE
);
3576 /* Check for invalid inherit disposition */
3577 if ((InheritDisposition
> ViewUnmap
) || (InheritDisposition
< ViewShare
))
3579 DPRINT1("Invalid inherit disposition\n");
3580 return STATUS_INVALID_PARAMETER_8
;
3583 /* Allow only valid allocation types */
3584 if (AllocationType
& ~ValidAllocationType
)
3586 DPRINT1("Invalid allocation type\n");
3587 return STATUS_INVALID_PARAMETER_9
;
3590 /* Convert the protection mask, and validate it */
3591 ProtectionMask
= MiMakeProtectionMask(Protect
);
3592 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3594 DPRINT1("Invalid page protection\n");
3595 return STATUS_INVALID_PAGE_PROTECTION
;
3598 /* Now convert the protection mask into desired section access mask */
3599 DesiredAccess
= MmMakeSectionAccess
[ProtectionMask
& 0x7];
3601 /* Assume no section offset */
3602 SafeSectionOffset
.QuadPart
= 0;
3607 /* Check for unsafe parameters */
3608 if (PreviousMode
!= KernelMode
)
3610 /* Probe the parameters */
3611 ProbeForWritePointer(BaseAddress
);
3612 ProbeForWriteSize_t(ViewSize
);
3615 /* Check if a section offset was given */
3618 /* Check for unsafe parameters and capture section offset */
3619 if (PreviousMode
!= KernelMode
) ProbeForWriteLargeInteger(SectionOffset
);
3620 SafeSectionOffset
= *SectionOffset
;
3623 /* Capture the other parameters */
3624 SafeBaseAddress
= *BaseAddress
;
3625 SafeViewSize
= *ViewSize
;
3627 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3629 /* Return the exception code */
3630 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3634 /* Check for kernel-mode address */
3635 if (SafeBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
3637 DPRINT1("Kernel base not allowed\n");
3638 return STATUS_INVALID_PARAMETER_3
;
3641 /* Check for range entering kernel-mode */
3642 if (((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
- (ULONG_PTR
)SafeBaseAddress
) < SafeViewSize
)
3644 DPRINT1("Overflowing into kernel base not allowed\n");
3645 return STATUS_INVALID_PARAMETER_3
;
3648 /* Check for invalid zero bits */
3651 if (ZeroBits
> MI_MAX_ZERO_BITS
)
3653 DPRINT1("Invalid zero bits\n");
3654 return STATUS_INVALID_PARAMETER_4
;
3657 if ((((ULONG_PTR
)SafeBaseAddress
<< ZeroBits
) >> ZeroBits
) != (ULONG_PTR
)SafeBaseAddress
)
3659 DPRINT1("Invalid zero bits\n");
3660 return STATUS_INVALID_PARAMETER_4
;
3663 if (((((ULONG_PTR
)SafeBaseAddress
+ SafeViewSize
) << ZeroBits
) >> ZeroBits
) != ((ULONG_PTR
)SafeBaseAddress
+ SafeViewSize
))
3665 DPRINT1("Invalid zero bits\n");
3666 return STATUS_INVALID_PARAMETER_4
;
3670 /* Reference the process */
3671 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3672 PROCESS_VM_OPERATION
,
3677 if (!NT_SUCCESS(Status
)) return Status
;
3679 /* Reference the section */
3680 Status
= ObReferenceObjectByHandle(SectionHandle
,
3682 MmSectionObjectType
,
3686 if (!NT_SUCCESS(Status
))
3688 ObDereferenceObject(Process
);
3692 if (!(AllocationType
& MEM_DOS_LIM
))
3694 /* Check for non-allocation-granularity-aligned BaseAddress */
3695 if (SafeBaseAddress
!= ALIGN_DOWN_POINTER_BY(SafeBaseAddress
, MM_VIRTMEM_GRANULARITY
))
3697 DPRINT("BaseAddress is not at 64-kilobyte address boundary.");
3698 ObDereferenceObject(Section
);
3699 ObDereferenceObject(Process
);
3700 return STATUS_MAPPED_ALIGNMENT
;
3703 /* Do the same for the section offset */
3704 if (SafeSectionOffset
.LowPart
!= ALIGN_DOWN_BY(SafeSectionOffset
.LowPart
, MM_VIRTMEM_GRANULARITY
))
3706 DPRINT("SectionOffset is not at 64-kilobyte address boundary.");
3707 ObDereferenceObject(Section
);
3708 ObDereferenceObject(Process
);
3709 return STATUS_MAPPED_ALIGNMENT
;
3713 /* Now do the actual mapping */
3714 Status
= MmMapViewOfSection(Section
,
3725 /* Return data only on success */
3726 if (NT_SUCCESS(Status
))
3728 /* Check if this is an image for the current process */
3729 if (MiIsRosSectionObject(Section
) &&
3730 (Section
->AllocationAttributes
& SEC_IMAGE
) &&
3731 (Process
== PsGetCurrentProcess()) &&
3732 (Status
!= STATUS_IMAGE_NOT_AT_BASE
))
3734 /* Notify the debugger */
3735 DbgkMapViewOfSection(Section
,
3737 SafeSectionOffset
.LowPart
,
3744 /* Return parameters to user */
3745 *BaseAddress
= SafeBaseAddress
;
3746 *ViewSize
= SafeViewSize
;
3747 if (SectionOffset
) *SectionOffset
= SafeSectionOffset
;
3749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3756 /* Dereference all objects and return status */
3757 ObDereferenceObject(Section
);
3758 ObDereferenceObject(Process
);
3764 NtUnmapViewOfSection(IN HANDLE ProcessHandle
,
3765 IN PVOID BaseAddress
)
3769 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3771 /* Don't allowing mapping kernel views */
3772 if ((PreviousMode
== UserMode
) && (BaseAddress
> MM_HIGHEST_USER_ADDRESS
))
3774 DPRINT1("Trying to unmap a kernel view\n");
3775 return STATUS_NOT_MAPPED_VIEW
;
3778 /* Reference the process */
3779 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3780 PROCESS_VM_OPERATION
,
3785 if (!NT_SUCCESS(Status
)) return Status
;
3787 /* Unmap the view */
3788 Status
= MiUnmapViewOfSection(Process
, BaseAddress
, 0);
3790 /* Dereference the process and return status */
3791 ObDereferenceObject(Process
);
3797 NtExtendSection(IN HANDLE SectionHandle
,
3798 IN OUT PLARGE_INTEGER NewMaximumSize
)
3800 LARGE_INTEGER SafeNewMaximumSize
;
3801 PROS_SECTION_OBJECT Section
;
3803 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3805 /* Check for user-mode parameters */
3806 if (PreviousMode
!= KernelMode
)
3811 /* Probe and capture the maximum size, it's both read and write */
3812 ProbeForWriteLargeInteger(NewMaximumSize
);
3813 SafeNewMaximumSize
= *NewMaximumSize
;
3815 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3817 /* Return the exception code */
3818 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3824 /* Just read the size directly */
3825 SafeNewMaximumSize
= *NewMaximumSize
;
3828 /* Reference the section */
3829 Status
= ObReferenceObjectByHandle(SectionHandle
,
3830 SECTION_EXTEND_SIZE
,
3831 MmSectionObjectType
,
3835 if (!NT_SUCCESS(Status
)) return Status
;
3837 /* Really this should go in MmExtendSection */
3838 if (!(Section
->AllocationAttributes
& SEC_FILE
))
3840 DPRINT1("Not extending a file\n");
3841 ObDereferenceObject(Section
);
3842 return STATUS_SECTION_NOT_EXTENDED
;
3845 /* FIXME: Do the work */
3847 /* Dereference the section */
3848 ObDereferenceObject(Section
);
3853 /* Write back the new size */
3854 *NewMaximumSize
= SafeNewMaximumSize
;
3856 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3862 /* Return the status */
3863 return STATUS_NOT_IMPLEMENTED
;