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 "../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_DECOMMIT
;
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
;
615 SegmentFlags
= Segment
->SegmentFlags
;
616 ControlArea
= Segment
->ControlArea
;
618 /* Make sure control area is on the right delete path */
619 ASSERT(ControlArea
->u
.Flags
.BeingDeleted
== 1);
620 ASSERT(ControlArea
->WritableUserReferences
== 0);
622 /* These things are not supported yet */
623 ASSERT(ControlArea
->DereferenceList
.Flink
== NULL
);
624 ASSERT(!(ControlArea
->u
.Flags
.Image
) & !(ControlArea
->u
.Flags
.File
));
625 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
626 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
628 /* Get the subsection and PTEs for this segment */
629 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
630 PointerPte
= Subsection
->SubsectionBase
;
631 LastPte
= PointerPte
+ Segment
->NonExtendedPtes
;
633 /* Lock the PFN database */
634 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
636 /* Check if the master PTE is invalid */
637 PteForProto
= MiAddressToPte(PointerPte
);
638 if (!PteForProto
->u
.Hard
.Valid
)
641 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
644 /* Loop all the segment PTEs */
645 while (PointerPte
< LastPte
)
647 /* Check if it's time to switch master PTEs if we passed a PDE boundary */
648 if (!((ULONG_PTR
)PointerPte
& (PD_SIZE
- 1)) &&
649 (PointerPte
!= Subsection
->SubsectionBase
))
651 /* Check if the master PTE is invalid */
652 PteForProto
= MiAddressToPte(PointerPte
);
653 if (!PteForProto
->u
.Hard
.Valid
)
656 MiMakeSystemAddressValidPfn(PointerPte
, OldIrql
);
660 /* This should be a prototype PTE */
661 TempPte
= *PointerPte
;
662 ASSERT(SegmentFlags
.LargePages
== 0);
663 ASSERT(TempPte
.u
.Hard
.Valid
== 0);
664 ASSERT(TempPte
.u
.Soft
.Prototype
== 1);
666 /* Zero the PTE and keep going */
667 PointerPte
->u
.Long
= 0;
671 /* Release the PFN lock */
672 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
674 /* Free the structures */
675 ExFreePool(ControlArea
);
681 MiCheckControlArea(IN PCONTROL_AREA ControlArea
,
684 BOOLEAN DeleteSegment
= FALSE
;
685 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
687 /* Check if this is the last reference or view */
688 if (!(ControlArea
->NumberOfMappedViews
) &&
689 !(ControlArea
->NumberOfSectionReferences
))
691 /* There should be no more user references either */
692 ASSERT(ControlArea
->NumberOfUserReferences
== 0);
694 /* Not yet supported */
695 ASSERT(ControlArea
->FilePointer
== NULL
);
697 /* The control area is being destroyed */
698 ControlArea
->u
.Flags
.BeingDeleted
= TRUE
;
699 DeleteSegment
= TRUE
;
702 /* Release the PFN lock */
703 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
705 /* Delete the segment if needed */
708 /* No more user write references at all */
709 ASSERT(ControlArea
->WritableUserReferences
== 0);
710 MiSegmentDelete(ControlArea
->Segment
);
716 MiDereferenceControlArea(IN PCONTROL_AREA ControlArea
)
720 /* Lock the PFN database */
721 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
723 /* Drop reference counts */
724 ControlArea
->NumberOfMappedViews
--;
725 ControlArea
->NumberOfUserReferences
--;
727 /* Check if it's time to delete the CA. This releases the lock */
728 MiCheckControlArea(ControlArea
, OldIrql
);
733 MiRemoveMappedView(IN PEPROCESS CurrentProcess
,
737 PCONTROL_AREA ControlArea
;
738 PETHREAD CurrentThread
= PsGetCurrentThread();
740 /* Get the control area */
741 ControlArea
= Vad
->ControlArea
;
743 /* We only support non-extendable, non-image, pagefile-backed regular sections */
744 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
745 ASSERT(Vad
->u2
.VadFlags2
.ExtendableFile
== FALSE
);
747 ASSERT(ControlArea
->FilePointer
== NULL
);
749 /* Delete the actual virtual memory pages */
750 MiDeleteVirtualAddresses(Vad
->StartingVpn
<< PAGE_SHIFT
,
751 (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1),
754 /* Release the working set */
755 MiUnlockProcessWorkingSetUnsafe(CurrentProcess
, CurrentThread
);
757 /* Lock the PFN database */
758 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
760 /* Remove references */
761 ControlArea
->NumberOfMappedViews
--;
762 ControlArea
->NumberOfUserReferences
--;
764 /* Check if it should be destroyed */
765 MiCheckControlArea(ControlArea
, OldIrql
);
770 MiUnmapViewOfSection(IN PEPROCESS Process
,
771 IN PVOID BaseAddress
,
774 PMEMORY_AREA MemoryArea
;
775 BOOLEAN Attached
= FALSE
;
778 PVOID DbgBase
= NULL
;
781 PETHREAD CurrentThread
= PsGetCurrentThread();
782 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
785 /* Check for Mm Region */
786 MemoryArea
= MmLocateMemoryAreaByAddress(&Process
->Vm
, BaseAddress
);
787 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
790 return MiRosUnmapViewOfSection(Process
, BaseAddress
, Flags
);
793 /* Check if we should attach to the process */
794 if (CurrentProcess
!= Process
)
796 /* The process is different, do an attach */
797 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
801 /* Check if we need to lock the address space */
802 if (!Flags
) MmLockAddressSpace(&Process
->Vm
);
804 /* Check if the process is already daed */
805 if (Process
->VmDeleted
)
808 DPRINT1("Process died!\n");
809 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
810 Status
= STATUS_PROCESS_IS_TERMINATING
;
814 /* Find the VAD for the address and make sure it's a section VAD */
815 Vad
= MiLocateAddress(BaseAddress
);
816 if (!(Vad
) || (Vad
->u
.VadFlags
.PrivateMemory
))
818 /* Couldn't find it, or invalid VAD, fail */
819 DPRINT1("No VAD or invalid VAD\n");
820 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
821 Status
= STATUS_NOT_MAPPED_VIEW
;
825 /* We should be attached */
826 ASSERT(Process
== PsGetCurrentProcess());
828 /* We need the base address for the debugger message on image-backed VADs */
829 if (Vad
->u
.VadFlags
.VadType
== VadImageMap
)
831 DbgBase
= (PVOID
)(Vad
->StartingVpn
>> PAGE_SHIFT
);
834 /* Compute the size of the VAD region */
835 RegionSize
= PAGE_SIZE
+ ((Vad
->EndingVpn
- Vad
->StartingVpn
) << PAGE_SHIFT
);
837 /* For SEC_NO_CHANGE sections, we need some extra checks */
838 if (Vad
->u
.VadFlags
.NoChange
== 1)
840 /* Are we allowed to mess with this VAD? */
841 Status
= MiCheckSecuredVad(Vad
,
842 (PVOID
)(Vad
->StartingVpn
>> PAGE_SHIFT
),
845 if (!NT_SUCCESS(Status
))
848 DPRINT1("Trying to unmap protected VAD!\n");
849 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
854 /* Not currently supported */
855 ASSERT(Vad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
857 /* FIXME: Remove VAD charges */
859 /* Lock the working set */
860 MiLockProcessWorkingSetUnsafe(Process
, CurrentThread
);
863 ASSERT(Process
->VadRoot
.NumberGenericTableElements
>= 1);
864 MiRemoveNode((PMMADDRESS_NODE
)Vad
, &Process
->VadRoot
);
866 /* Remove the PTEs for this view, which also releases the working set lock */
867 MiRemoveMappedView(Process
, Vad
);
869 /* FIXME: Remove commitment */
871 /* Update performance counter and release the lock */
872 Process
->VirtualSize
-= RegionSize
;
873 if (!Flags
) MmUnlockAddressSpace(&Process
->Vm
);
875 /* Destroy the VAD and return success */
877 Status
= STATUS_SUCCESS
;
879 /* Failure and success case -- send debugger message, detach, and return */
881 if (DbgBase
) DbgkUnMapViewOfSection(DbgBase
);
882 if (Attached
) KeUnstackDetachProcess(&ApcState
);
888 MiSessionCommitPageTables(IN PVOID StartVa
,
893 PMMPTE StartPde
, EndPde
;
894 MMPTE TempPte
= ValidKernelPdeLocal
;
896 PFN_NUMBER PageCount
= 0, ActualPages
= 0, PageFrameNumber
;
898 /* Windows sanity checks */
899 ASSERT(StartVa
>= (PVOID
)MmSessionBase
);
900 ASSERT(EndVa
< (PVOID
)MiSessionSpaceEnd
);
901 ASSERT(PAGE_ALIGN(EndVa
) == EndVa
);
903 /* Get the start and end PDE, then loop each one */
904 StartPde
= MiAddressToPde(StartVa
);
905 EndPde
= MiAddressToPde((PVOID
)((ULONG_PTR
)EndVa
- 1));
906 Index
= ((ULONG_PTR
)StartVa
- (ULONG_PTR
)MmSessionBase
) >> 22;
907 while (StartPde
<= EndPde
)
910 /* If we don't already have a page table for it, increment count */
911 if (MmSessionSpace
->PageTables
[Index
].u
.Long
== 0) PageCount
++;
913 /* Move to the next one */
918 /* If there's no page tables to create, bail out */
919 if (PageCount
== 0) return STATUS_SUCCESS
;
921 /* Reset the start PDE and index */
922 StartPde
= MiAddressToPde(StartVa
);
923 Index
= ((ULONG_PTR
)StartVa
- (ULONG_PTR
)MmSessionBase
) >> 22;
925 /* Loop each PDE while holding the working set lock */
926 // MiLockWorkingSet(PsGetCurrentThread(),
927 // &MmSessionSpace->GlobalVirtualAddress->Vm);
929 while (StartPde
<= EndPde
)
931 /* Check if we already have a page table */
932 if (MmSessionSpace
->PageTables
[Index
].u
.Long
== 0)
934 /* We don't, so the PDE shouldn't be ready yet */
935 ASSERT(StartPde
->u
.Hard
.Valid
== 0);
937 /* ReactOS check to avoid MiEnsureAvailablePageOrWait */
938 ASSERT(MmAvailablePages
>= 32);
940 /* Acquire the PFN lock and grab a zero page */
941 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
942 Color
= (++MmSessionSpace
->Color
) & MmSecondaryColorMask
;
943 PageFrameNumber
= MiRemoveZeroPage(Color
);
944 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
945 MI_WRITE_VALID_PTE(StartPde
, TempPte
);
947 /* Write the page table in session space structure */
948 ASSERT(MmSessionSpace
->PageTables
[Index
].u
.Long
== 0);
949 MmSessionSpace
->PageTables
[Index
] = TempPte
;
951 /* Initialize the PFN */
952 MiInitializePfnForOtherProcess(PageFrameNumber
,
954 MmSessionSpace
->SessionPageDirectoryIndex
);
956 /* And now release the lock */
957 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
959 /* Get the PFN entry and make sure there's no event for it */
960 Pfn1
= MI_PFN_ELEMENT(PageFrameNumber
);
961 ASSERT(Pfn1
->u1
.Event
== NULL
);
963 /* Increment the number of pages */
967 /* Move to the next PDE */
973 /* Make sure we didn't do more pages than expected */
974 ASSERT(ActualPages
<= PageCount
);
976 /* Release the working set lock */
977 // MiUnlockWorkingSet(PsGetCurrentThread(),
978 // &MmSessionSpace->GlobalVirtualAddress->Vm);
981 /* If we did at least one page... */
984 /* Update the performance counters! */
985 InterlockedExchangeAddSizeT(&MmSessionSpace
->NonPageablePages
, ActualPages
);
986 InterlockedExchangeAddSizeT(&MmSessionSpace
->CommittedPages
, ActualPages
);
990 return STATUS_SUCCESS
;
995 MiMapViewInSystemSpace(IN PVOID Section
,
996 IN PMMSESSION Session
,
997 OUT PVOID
*MappedBase
,
998 IN OUT PSIZE_T ViewSize
)
1001 PCONTROL_AREA ControlArea
;
1002 ULONG Buckets
, SectionSize
;
1006 /* Get the control area, check for any flags ARM3 doesn't yet support */
1007 ControlArea
= ((PSECTION
)Section
)->Segment
->ControlArea
;
1008 ASSERT(ControlArea
->u
.Flags
.Image
== 0);
1009 ASSERT(ControlArea
->FilePointer
== NULL
);
1010 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
1011 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
1012 ASSERT(ControlArea
->u
.Flags
.WasPurged
== 0);
1014 /* Increase the reference and map count on the control area, no purges yet */
1015 Status
= MiCheckPurgeAndUpMapCount(ControlArea
, FALSE
);
1016 ASSERT(NT_SUCCESS(Status
));
1018 /* Get the section size at creation time */
1019 SectionSize
= ((PSECTION
)Section
)->SizeOfSection
.LowPart
;
1021 /* If the caller didn't specify a view size, assume the whole section */
1022 if (!(*ViewSize
)) *ViewSize
= SectionSize
;
1024 /* Check if the caller wanted a larger section than the view */
1025 if (*ViewSize
> SectionSize
)
1028 DPRINT1("View is too large\n");
1029 MiDereferenceControlArea(ControlArea
);
1030 return STATUS_INVALID_VIEW_SIZE
;
1033 /* Get the number of 64K buckets required for this mapping */
1034 Buckets
= (ULONG
)(*ViewSize
/ MI_SYSTEM_VIEW_BUCKET_SIZE
);
1035 if (*ViewSize
& (MI_SYSTEM_VIEW_BUCKET_SIZE
- 1)) Buckets
++;
1037 /* Check if the view is more than 4GB large */
1038 if (Buckets
>= MI_SYSTEM_VIEW_BUCKET_SIZE
)
1041 DPRINT1("View is too large\n");
1042 MiDereferenceControlArea(ControlArea
);
1043 return STATUS_INVALID_VIEW_SIZE
;
1046 /* Insert this view into system space and get a base address for it */
1047 Base
= MiInsertInSystemSpace(Session
, Buckets
, ControlArea
);
1051 DPRINT1("Out of system space\n");
1052 MiDereferenceControlArea(ControlArea
);
1053 return STATUS_NO_MEMORY
;
1056 /* What's the underlying session? */
1057 if (Session
== &MmSession
)
1059 /* Create the PDEs needed for this mapping, and double-map them if needed */
1060 MiFillSystemPageDirectory(Base
, Buckets
* MI_SYSTEM_VIEW_BUCKET_SIZE
);
1061 Status
= STATUS_SUCCESS
;
1065 /* Create the PDEs needed for this mapping */
1066 Status
= MiSessionCommitPageTables(Base
,
1067 (PVOID
)((ULONG_PTR
)Base
+
1068 Buckets
* MI_SYSTEM_VIEW_BUCKET_SIZE
));
1069 NT_ASSERT(NT_SUCCESS(Status
));
1072 /* Create the actual prototype PTEs for this mapping */
1073 Status
= MiAddMappedPtes(MiAddressToPte(Base
),
1074 BYTES_TO_PAGES(*ViewSize
),
1076 ASSERT(NT_SUCCESS(Status
));
1078 /* Return the base adress of the mapping and success */
1080 return STATUS_SUCCESS
;
1085 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea
,
1086 IN PEPROCESS Process
,
1087 IN PVOID
*BaseAddress
,
1088 IN PLARGE_INTEGER SectionOffset
,
1089 IN PSIZE_T ViewSize
,
1090 IN PSECTION Section
,
1091 IN SECTION_INHERIT InheritDisposition
,
1092 IN ULONG ProtectionMask
,
1093 IN SIZE_T CommitSize
,
1094 IN ULONG_PTR ZeroBits
,
1095 IN ULONG AllocationType
)
1098 PETHREAD Thread
= PsGetCurrentThread();
1099 ULONG_PTR StartAddress
, EndingAddress
;
1100 PSUBSECTION Subsection
;
1102 PFN_NUMBER PteOffset
;
1104 ULONG QuotaCharge
= 0, QuotaExcess
= 0;
1105 PMMPTE PointerPte
, LastPte
;
1108 /* Get the segment for this section */
1109 Segment
= ControlArea
->Segment
;
1111 /* One can only reserve a file-based mapping, not shared memory! */
1112 if ((AllocationType
& MEM_RESERVE
) && !(ControlArea
->FilePointer
))
1114 return STATUS_INVALID_PARAMETER_9
;
1117 /* This flag determines alignment, but ARM3 does not yet support it */
1118 ASSERT((AllocationType
& MEM_DOS_LIM
) == 0);
1120 /* First, increase the map count. No purging is supported yet */
1121 Status
= MiCheckPurgeAndUpMapCount(ControlArea
, FALSE
);
1122 if (!NT_SUCCESS(Status
)) return Status
;
1124 /* Check if the caller specified the view size */
1127 /* The caller did not, so pick a 64K aligned view size based on the offset */
1128 SectionOffset
->LowPart
&= ~(_64K
- 1);
1129 *ViewSize
= (SIZE_T
)(Section
->SizeOfSection
.QuadPart
- SectionOffset
->QuadPart
);
1133 /* A size was specified, align it to a 64K boundary */
1134 *ViewSize
+= SectionOffset
->LowPart
& (_64K
- 1);
1136 /* Align the offset as well to make this an aligned map */
1137 SectionOffset
->LowPart
&= ~((ULONG
)_64K
- 1);
1140 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1141 ASSERT((SectionOffset
->LowPart
& ((ULONG
)_64K
- 1)) == 0);
1143 /* It's illegal to try to map more than 2GB */
1144 /* FIXME: Should dereference the control area */
1145 if (*ViewSize
>= 0x80000000) return STATUS_INVALID_VIEW_SIZE
;
1147 /* Windows ASSERTs for this flag */
1148 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
1150 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1151 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
1152 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
1154 /* Sections with extended segments are not supported in ARM3 */
1155 ASSERT(Segment
->SegmentFlags
.TotalNumberOfPtes4132
== 0);
1157 /* Within this section, figure out which PTEs will describe the view */
1158 PteOffset
= (PFN_NUMBER
)(SectionOffset
->QuadPart
>> PAGE_SHIFT
);
1160 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1161 ASSERT(PteOffset
< Segment
->TotalNumberOfPtes
);
1162 ASSERT(((SectionOffset
->QuadPart
+ *ViewSize
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
) >= PteOffset
);
1164 /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1165 ASSERT(PteOffset
< Subsection
->PtesInSubsection
);
1167 /* In ARM3, only page-file backed sections (shared memory) are supported now */
1168 ASSERT(ControlArea
->FilePointer
== NULL
);
1170 /* Windows ASSERTs for this too -- there must be a subsection base address */
1171 ASSERT(Subsection
->SubsectionBase
!= NULL
);
1173 /* Compute how much commit space the segment will take */
1174 if ((CommitSize
) && (Segment
->NumberOfCommittedPages
< Segment
->TotalNumberOfPtes
))
1176 PointerPte
= &Subsection
->SubsectionBase
[PteOffset
];
1177 LastPte
= PointerPte
+ BYTES_TO_PAGES(CommitSize
);
1178 QuotaCharge
= (ULONG
)(LastPte
- PointerPte
);
1181 /* ARM3 does not currently support large pages */
1182 ASSERT(Segment
->SegmentFlags
.LargePages
== 0);
1184 /* Did the caller specify an address? */
1185 if (!(*BaseAddress
) && !(Section
->Address
.StartingVpn
))
1187 /* ARM3 does not support these flags yet */
1188 ASSERT(Process
->VmTopDown
== 0);
1189 ASSERT(ZeroBits
== 0);
1191 /* Which way should we search? */
1192 if (AllocationType
& MEM_TOP_DOWN
)
1194 /* No, find an address top-down */
1195 Status
= MiFindEmptyAddressRangeDownTree(*ViewSize
,
1196 (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
,
1200 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
);
1201 ASSERT(NT_SUCCESS(Status
));
1205 /* No, find an address bottom-up */
1206 Status
= MiFindEmptyAddressRangeInTree(*ViewSize
,
1209 (PMMADDRESS_NODE
*)&Process
->VadFreeHint
,
1211 ASSERT(NT_SUCCESS(Status
));
1214 /* Get the ending address, which is the last piece we need for the VAD */
1215 EndingAddress
= (StartAddress
+ *ViewSize
- 1) | (PAGE_SIZE
- 1);
1219 /* Is it SEC_BASED, or did the caller manually specify an address? */
1220 if (!(*BaseAddress
))
1222 /* It is a SEC_BASED mapping, use the address that was generated */
1223 StartAddress
= Section
->Address
.StartingVpn
+ SectionOffset
->LowPart
;
1224 DPRINT("BASED: 0x%p\n", StartAddress
);
1228 /* Just align what the caller gave us */
1229 StartAddress
= ROUND_UP((ULONG_PTR
)*BaseAddress
, _64K
);
1232 /* Get the ending address, which is the last piece we need for the VAD */
1233 EndingAddress
= (StartAddress
+ *ViewSize
- 1) | (PAGE_SIZE
- 1);
1235 /* Make sure it doesn't conflict with an existing allocation */
1236 if (MiCheckForConflictingNode(StartAddress
>> PAGE_SHIFT
,
1237 EndingAddress
>> PAGE_SHIFT
,
1240 DPRINT1("Conflict with SEC_BASED or manually based section!\n");
1241 MiDereferenceControlArea(ControlArea
);
1242 return STATUS_CONFLICTING_ADDRESSES
;
1246 /* A VAD can now be allocated. Do so and zero it out */
1247 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1248 ASSERT((AllocationType
& MEM_RESERVE
) == 0); /* ARM3 does not support this */
1249 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'ldaV');
1252 MiDereferenceControlArea(ControlArea
);
1253 return STATUS_INSUFFICIENT_RESOURCES
;
1255 RtlZeroMemory(Vad
, sizeof(MMVAD_LONG
));
1256 Vad
->u4
.Banked
= (PVOID
)0xDEADBABE;
1258 /* Write all the data required in the VAD for handling a fault */
1259 Vad
->StartingVpn
= StartAddress
>> PAGE_SHIFT
;
1260 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
1261 Vad
->ControlArea
= ControlArea
;
1262 Vad
->u
.VadFlags
.Protection
= ProtectionMask
;
1263 Vad
->u2
.VadFlags2
.FileOffset
= (ULONG
)(SectionOffset
->QuadPart
>> 16);
1264 Vad
->u2
.VadFlags2
.Inherit
= (InheritDisposition
== ViewShare
);
1265 if ((AllocationType
& SEC_NO_CHANGE
) || (Section
->u
.Flags
.NoChange
))
1267 /* This isn't really implemented yet, but handle setting the flag */
1268 Vad
->u
.VadFlags
.NoChange
= 1;
1269 Vad
->u2
.VadFlags2
.SecNoChange
= 1;
1272 /* Finally, write down the first and last prototype PTE */
1273 Vad
->FirstPrototypePte
= &Subsection
->SubsectionBase
[PteOffset
];
1274 PteOffset
+= (Vad
->EndingVpn
- Vad
->StartingVpn
);
1275 ASSERT(PteOffset
< Subsection
->PtesInSubsection
);
1276 Vad
->LastContiguousPte
= &Subsection
->SubsectionBase
[PteOffset
];
1278 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1279 ASSERT(Vad
->FirstPrototypePte
<= Vad
->LastContiguousPte
);
1281 /* FIXME: Should setup VAD bitmap */
1282 Status
= STATUS_SUCCESS
;
1284 /* Pretend as if we own the working set */
1285 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1287 /* Insert the VAD */
1288 MiInsertVad((PMMVAD
)Vad
, Process
);
1290 /* Release the working set */
1291 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1293 /* Windows stores this for accounting purposes, do so as well */
1294 if (!Segment
->u2
.FirstMappedVa
) Segment
->u2
.FirstMappedVa
= (PVOID
)StartAddress
;
1296 /* Check if anything was committed */
1299 /* Set the start and end PTE addresses, and pick the template PTE */
1300 PointerPte
= Vad
->FirstPrototypePte
;
1301 LastPte
= PointerPte
+ BYTES_TO_PAGES(CommitSize
);
1302 TempPte
= Segment
->SegmentPteTemplate
;
1304 /* Acquire the commit lock and loop all prototype PTEs to be committed */
1305 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex
);
1306 while (PointerPte
< LastPte
)
1308 /* Make sure the PTE is already invalid */
1309 if (PointerPte
->u
.Long
== 0)
1311 /* And write the invalid PTE */
1312 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1316 /* The PTE is valid, so skip it */
1320 /* Move to the next PTE */
1324 /* Now check how many pages exactly we committed, and update accounting */
1325 ASSERT(QuotaCharge
>= QuotaExcess
);
1326 QuotaCharge
-= QuotaExcess
;
1327 Segment
->NumberOfCommittedPages
+= QuotaCharge
;
1328 ASSERT(Segment
->NumberOfCommittedPages
<= Segment
->TotalNumberOfPtes
);
1330 /* Now that we're done, release the lock */
1331 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex
);
1334 /* Finally, let the caller know where, and for what size, the view was mapped */
1335 *ViewSize
= (ULONG_PTR
)EndingAddress
- (ULONG_PTR
)StartAddress
+ 1;
1336 *BaseAddress
= (PVOID
)StartAddress
;
1337 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress
, *ViewSize
);
1338 return STATUS_SUCCESS
;
1343 MiSubsectionConsistent(IN PSUBSECTION Subsection
)
1345 /* ReactOS only supports systems with 4K pages and 4K sectors */
1346 ASSERT(Subsection
->u
.SubsectionFlags
.SectorEndOffset
== 0);
1348 /* Therefore, then number of PTEs should be equal to the number of sectors */
1349 if (Subsection
->NumberOfFullSectors
!= Subsection
->PtesInSubsection
)
1351 /* Break and warn if this is inconsistent */
1352 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1353 Subsection
->NumberOfFullSectors
, Subsection
->PtesInSubsection
);
1360 MiCreateDataFileMap(IN PFILE_OBJECT File
,
1361 OUT PSEGMENT
*Segment
,
1362 IN PSIZE_T MaximumSize
,
1363 IN ULONG SectionPageProtection
,
1364 IN ULONG AllocationAttributes
,
1365 IN ULONG IgnoreFileSizing
)
1367 /* Not yet implemented */
1370 return STATUS_NOT_IMPLEMENTED
;
1375 MiCreatePagingFileMap(OUT PSEGMENT
*Segment
,
1376 IN PSIZE_T MaximumSize
,
1377 IN ULONG ProtectionMask
,
1378 IN ULONG AllocationAttributes
)
1384 PCONTROL_AREA ControlArea
;
1385 PSEGMENT NewSegment
;
1386 PSUBSECTION Subsection
;
1389 /* No large pages in ARM3 yet */
1390 ASSERT((AllocationAttributes
& SEC_LARGE_PAGES
) == 0);
1392 /* Pagefile-backed sections need a known size */
1393 if (!(*MaximumSize
)) return STATUS_INVALID_PARAMETER_4
;
1395 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1396 SizeLimit
= MAXULONG_PTR
- sizeof(SEGMENT
);
1397 SizeLimit
/= sizeof(MMPTE
);
1398 SizeLimit
<<= PAGE_SHIFT
;
1400 /* Fail if this size is too big */
1401 if (*MaximumSize
> SizeLimit
) return STATUS_SECTION_TOO_BIG
;
1403 /* Calculate how many Prototype PTEs will be needed */
1404 PteCount
= (PFN_COUNT
)((*MaximumSize
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
);
1406 /* For commited memory, we must have a valid protection mask */
1407 if (AllocationAttributes
& SEC_COMMIT
) ASSERT(ProtectionMask
!= 0);
1409 /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1410 NewSegment
= ExAllocatePoolWithTag(PagedPool
,
1412 sizeof(MMPTE
) * (PteCount
- 1),
1415 *Segment
= NewSegment
;
1417 /* Now allocate the control area, which has the subsection structure */
1418 ControlArea
= ExAllocatePoolWithTag(NonPagedPool
,
1419 sizeof(CONTROL_AREA
) + sizeof(SUBSECTION
),
1421 ASSERT(ControlArea
);
1423 /* And zero it out, filling the basic segmnet pointer and reference fields */
1424 RtlZeroMemory(ControlArea
, sizeof(CONTROL_AREA
) + sizeof(SUBSECTION
));
1425 ControlArea
->Segment
= NewSegment
;
1426 ControlArea
->NumberOfSectionReferences
= 1;
1427 ControlArea
->NumberOfUserReferences
= 1;
1429 /* Convert allocation attributes to control area flags */
1430 if (AllocationAttributes
& SEC_BASED
) ControlArea
->u
.Flags
.Based
= 1;
1431 if (AllocationAttributes
& SEC_RESERVE
) ControlArea
->u
.Flags
.Reserve
= 1;
1432 if (AllocationAttributes
& SEC_COMMIT
) ControlArea
->u
.Flags
.Commit
= 1;
1434 /* The subsection follows, write the mask, PTE count and point back to the CA */
1435 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
1436 Subsection
->ControlArea
= ControlArea
;
1437 Subsection
->PtesInSubsection
= PteCount
;
1438 Subsection
->u
.SubsectionFlags
.Protection
= ProtectionMask
;
1440 /* Zero out the segment's prototype PTEs, and link it with the control area */
1441 PointerPte
= &NewSegment
->ThePtes
[0];
1442 RtlZeroMemory(NewSegment
, sizeof(SEGMENT
));
1443 NewSegment
->PrototypePte
= PointerPte
;
1444 NewSegment
->ControlArea
= ControlArea
;
1446 /* Save some extra accounting data for the segment as well */
1447 NewSegment
->u1
.CreatingProcess
= PsGetCurrentProcess();
1448 NewSegment
->SizeOfSegment
= PteCount
* PAGE_SIZE
;
1449 NewSegment
->TotalNumberOfPtes
= PteCount
;
1450 NewSegment
->NonExtendedPtes
= PteCount
;
1452 /* The subsection's base address is the first Prototype PTE in the segment */
1453 Subsection
->SubsectionBase
= PointerPte
;
1455 /* Start with an empty PTE, unless this is a commit operation */
1457 if (AllocationAttributes
& SEC_COMMIT
)
1459 /* In which case, write down the protection mask in the Prototype PTEs */
1460 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
1462 /* For accounting, also mark these pages as being committed */
1463 NewSegment
->NumberOfCommittedPages
= PteCount
;
1466 /* The template PTE itself for the segment should also have the mask set */
1467 NewSegment
->SegmentPteTemplate
.u
.Soft
.Protection
= ProtectionMask
;
1469 /* Write out the prototype PTEs, for now they're simply demand zero */
1471 RtlFillMemoryUlonglong(PointerPte
, PteCount
* sizeof(MMPTE
), TempPte
.u
.Long
);
1473 RtlFillMemoryUlong(PointerPte
, PteCount
* sizeof(MMPTE
), TempPte
.u
.Long
);
1475 return STATUS_SUCCESS
;
1480 MmGetFileObjectForSection(IN PVOID SectionObject
)
1482 PSECTION_OBJECT Section
;
1483 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
1484 ASSERT(SectionObject
!= NULL
);
1486 /* Check if it's an ARM3, or ReactOS section */
1487 if (MiIsRosSectionObject(SectionObject
) == FALSE
)
1489 /* Return the file pointer stored in the control area */
1490 Section
= SectionObject
;
1491 return Section
->Segment
->ControlArea
->FilePointer
;
1494 /* Return the file object */
1495 return ((PROS_SECTION_OBJECT
)SectionObject
)->FileObject
;
1500 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject
,
1501 OUT POBJECT_NAME_INFORMATION
*ModuleName
)
1503 POBJECT_NAME_INFORMATION ObjectNameInfo
;
1507 /* Allocate memory for our structure */
1508 ObjectNameInfo
= ExAllocatePoolWithTag(PagedPool
, 1024, TAG_MM
);
1509 if (!ObjectNameInfo
) return STATUS_NO_MEMORY
;
1511 /* Query the name */
1512 Status
= ObQueryNameString(FileObject
,
1516 if (!NT_SUCCESS(Status
))
1518 /* Failed, free memory */
1519 DPRINT1("Name query failed\n");
1520 ExFreePoolWithTag(ObjectNameInfo
, TAG_MM
);
1526 *ModuleName
= ObjectNameInfo
;
1527 return STATUS_SUCCESS
;
1532 MmGetFileNameForSection(IN PVOID Section
,
1533 OUT POBJECT_NAME_INFORMATION
*ModuleName
)
1535 PFILE_OBJECT FileObject
;
1537 /* Make sure it's an image section */
1538 if (MiIsRosSectionObject(Section
) == FALSE
)
1540 /* Check ARM3 Section flag */
1541 if (((PSECTION
)Section
)->u
.Flags
.Image
== 0)
1543 /* It's not, fail */
1544 DPRINT1("Not an image section\n");
1545 return STATUS_SECTION_NOT_IMAGE
;
1548 else if (!(((PROS_SECTION_OBJECT
)Section
)->AllocationAttributes
& SEC_IMAGE
))
1550 /* It's not, fail */
1551 DPRINT1("Not an image section\n");
1552 return STATUS_SECTION_NOT_IMAGE
;
1555 /* Get the file object */
1556 FileObject
= MmGetFileObjectForSection(Section
);
1557 return MmGetFileNameForFileObject(FileObject
, ModuleName
);
1562 MmGetFileNameForAddress(IN PVOID Address
,
1563 OUT PUNICODE_STRING ModuleName
)
1566 PMEMORY_AREA MemoryArea
;
1567 POBJECT_NAME_INFORMATION ModuleNameInformation
;
1570 PFILE_OBJECT FileObject
= NULL
;
1572 PCONTROL_AREA ControlArea
;
1574 /* Lock address space */
1575 AddressSpace
= MmGetCurrentAddressSpace();
1576 MmLockAddressSpace(AddressSpace
);
1578 /* Locate the memory area for the process by address */
1579 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, Address
);
1582 /* Fail, the address does not exist */
1584 DPRINT1("Invalid address\n");
1585 MmUnlockAddressSpace(AddressSpace
);
1586 return STATUS_INVALID_ADDRESS
;
1589 /* Check if it's a section view (RosMm section) or ARM3 section */
1590 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
1592 /* Get the section pointer to the SECTION_OBJECT */
1593 Section
= MemoryArea
->Data
.SectionData
.Section
;
1595 /* Unlock address space */
1596 MmUnlockAddressSpace(AddressSpace
);
1598 /* Get the filename of the section */
1599 Status
= MmGetFileNameForSection(Section
, &ModuleNameInformation
);
1601 else if (MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
)
1604 Vad
= MiLocateAddress(Address
);
1605 if (!Vad
) goto InvalidAddress
;
1607 /* Make sure it's not a VM VAD */
1608 if (Vad
->u
.VadFlags
.PrivateMemory
== 1)
1611 DPRINT1("Address is not a section\n");
1612 MmUnlockAddressSpace(AddressSpace
);
1613 return STATUS_SECTION_NOT_IMAGE
;
1616 /* Get the control area */
1617 ControlArea
= Vad
->ControlArea
;
1618 if (!(ControlArea
) || !(ControlArea
->u
.Flags
.Image
)) goto NotSection
;
1620 /* Get the file object */
1621 FileObject
= ControlArea
->FilePointer
;
1622 ASSERT(FileObject
!= NULL
);
1623 ObReferenceObject(FileObject
);
1625 /* Unlock address space */
1626 MmUnlockAddressSpace(AddressSpace
);
1628 /* Get the filename of the file object */
1629 Status
= MmGetFileNameForFileObject(FileObject
, &ModuleNameInformation
);
1631 /* Dereference it */
1632 ObDereferenceObject(FileObject
);
1636 /* Trying to access virtual memory or something */
1637 goto InvalidAddress
;
1640 /* Check if we were able to get the file object name */
1641 if (NT_SUCCESS(Status
))
1643 /* Init modulename */
1644 RtlCreateUnicodeString(ModuleName
,
1645 ModuleNameInformation
->Name
.Buffer
);
1647 /* Free temp taged buffer from MmGetFileNameForFileObject() */
1648 ExFreePoolWithTag(ModuleNameInformation
, TAG_MM
);
1649 DPRINT("Found ModuleName %S by address %p\n", ModuleName
->Buffer
, Address
);
1658 MiQueryMemorySectionName(IN HANDLE ProcessHandle
,
1659 IN PVOID BaseAddress
,
1660 OUT PVOID MemoryInformation
,
1661 IN SIZE_T MemoryInformationLength
,
1662 OUT PSIZE_T ReturnLength
)
1666 WCHAR ModuleFileNameBuffer
[MAX_PATH
] = {0};
1667 UNICODE_STRING ModuleFileName
;
1668 PMEMORY_SECTION_NAME SectionName
= NULL
;
1669 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1671 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1672 PROCESS_QUERY_INFORMATION
,
1678 if (!NT_SUCCESS(Status
))
1680 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status
);
1684 RtlInitEmptyUnicodeString(&ModuleFileName
, ModuleFileNameBuffer
, sizeof(ModuleFileNameBuffer
));
1685 Status
= MmGetFileNameForAddress(BaseAddress
, &ModuleFileName
);
1687 if (NT_SUCCESS(Status
))
1689 SectionName
= MemoryInformation
;
1690 if (PreviousMode
!= KernelMode
)
1694 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
1695 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
1696 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
1698 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
1701 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1703 Status
= _SEH2_GetExceptionCode();
1709 RtlInitUnicodeString(&SectionName
->SectionFileName
, SectionName
->NameBuffer
);
1710 SectionName
->SectionFileName
.MaximumLength
= (USHORT
)MemoryInformationLength
;
1711 RtlCopyUnicodeString(&SectionName
->SectionFileName
, &ModuleFileName
);
1713 if (ReturnLength
) *ReturnLength
= ModuleFileName
.Length
;
1717 ObDereferenceObject(Process
);
1723 MiFlushTbAndCapture(IN PMMVAD FoundVad
,
1724 IN PMMPTE PointerPte
,
1725 IN ULONG ProtectionMask
,
1727 IN BOOLEAN CaptureDirtyBit
)
1729 MMPTE TempPte
, PreviousPte
;
1731 BOOLEAN RebuildPte
= FALSE
;
1734 // User for sanity checking later on
1736 PreviousPte
= *PointerPte
;
1739 // Build the PTE and acquire the PFN lock
1741 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1744 PreviousPte
.u
.Hard
.PageFrameNumber
);
1745 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1748 // We don't support I/O mappings in this path yet
1750 ASSERT(Pfn1
!= NULL
);
1751 ASSERT(Pfn1
->u3
.e1
.CacheAttribute
!= MiWriteCombined
);
1754 // Make sure new protection mask doesn't get in conflict and fix it if it does
1756 if (Pfn1
->u3
.e1
.CacheAttribute
== MiCached
)
1759 // This is a cached PFN
1761 if (ProtectionMask
& (MM_NOCACHE
| MM_NOACCESS
))
1764 ProtectionMask
&= ~(MM_NOCACHE
| MM_NOACCESS
);
1767 else if (Pfn1
->u3
.e1
.CacheAttribute
== MiNonCached
)
1770 // This is a non-cached PFN
1772 if ((ProtectionMask
& (MM_NOCACHE
| MM_NOACCESS
)) != MM_NOCACHE
)
1775 ProtectionMask
&= ~MM_NOACCESS
;
1776 ProtectionMask
|= MM_NOCACHE
;
1782 MI_MAKE_HARDWARE_PTE_USER(&TempPte
,
1785 PreviousPte
.u
.Hard
.PageFrameNumber
);
1789 // Write the new PTE, making sure we are only changing the bits
1791 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1792 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
1793 ASSERT(PointerPte
->u
.Hard
.PageFrameNumber
== TempPte
.u
.Hard
.PageFrameNumber
);
1794 *PointerPte
= TempPte
;
1799 ASSERT(PreviousPte
.u
.Hard
.Valid
== 1);
1801 ASSERT(PreviousPte
.u
.Hard
.Valid
== 1);
1804 // Windows updates the relevant PFN1 information, we currently don't.
1806 if (CaptureDirtyBit
) DPRINT1("Warning, not handling dirty bit\n");
1809 // Not supported in ARM3
1811 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadWriteWatch
);
1814 // Release the PFN lock, we are done
1816 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1820 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
1824 MiSetProtectionOnSection(IN PEPROCESS Process
,
1826 IN PVOID StartingAddress
,
1827 IN PVOID EndingAddress
,
1828 IN ULONG NewProtect
,
1829 OUT PULONG CapturedOldProtect
,
1830 IN ULONG DontCharge
,
1833 PMMPTE PointerPte
, LastPte
;
1834 MMPTE TempPte
, PteContents
;
1837 ULONG ProtectionMask
, QuotaCharge
= 0;
1838 PETHREAD Thread
= PsGetCurrentThread();
1842 // Tell caller nothing is being locked
1847 // This function should only be used for section VADs. Windows ASSERT */
1849 ASSERT(FoundVad
->u
.VadFlags
.PrivateMemory
== 0);
1852 // We don't support these features in ARM3
1854 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadImageMap
);
1855 ASSERT(FoundVad
->u2
.VadFlags2
.CopyOnWrite
== 0);
1858 // Convert and validate the protection mask
1860 ProtectionMask
= MiMakeProtectionMask(NewProtect
);
1861 if (ProtectionMask
== MM_INVALID_PROTECTION
)
1863 DPRINT1("Invalid section protect\n");
1864 return STATUS_INVALID_PAGE_PROTECTION
;
1868 // Get the PTE and PDE for the address, as well as the final PTE
1870 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1871 PointerPde
= MiAddressToPde(StartingAddress
);
1872 PointerPte
= MiAddressToPte(StartingAddress
);
1873 LastPte
= MiAddressToPte(EndingAddress
);
1876 // Make the PDE valid, and check the status of the first PTE
1878 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1879 if (PointerPte
->u
.Long
)
1882 // Not supported in ARM3
1884 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadRotatePhysical
);
1887 // Capture the page protection and make the PDE valid
1889 *CapturedOldProtect
= MiGetPageProtection(PointerPte
);
1890 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1895 // Only pagefile-backed section VADs are supported for now
1897 ASSERT(FoundVad
->u
.VadFlags
.VadType
!= VadImageMap
);
1900 // Grab the old protection from the VAD itself
1902 *CapturedOldProtect
= MmProtectToValue
[FoundVad
->u
.VadFlags
.Protection
];
1906 // Loop all the PTEs now
1908 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1909 while (PointerPte
<= LastPte
)
1912 // Check if we've crossed a PDE boundary and make the new PDE valid too
1914 if ((((ULONG_PTR
)PointerPte
) & (SYSTEM_PD_SIZE
- 1)) == 0)
1916 PointerPde
= MiAddressToPte(PointerPte
);
1917 MiMakePdeExistAndMakeValid(PointerPde
, Process
, MM_NOIRQL
);
1921 // Capture the PTE and see what we're dealing with
1923 PteContents
= *PointerPte
;
1924 if (PteContents
.u
.Long
== 0)
1927 // This used to be a zero PTE and it no longer is, so we must add a
1928 // reference to the pagetable.
1930 MiIncrementPageTableReferences(MiPteToAddress(PointerPte
));
1933 // Create the demand-zero prototype PTE
1935 TempPte
= PrototypePte
;
1936 TempPte
.u
.Soft
.Protection
= ProtectionMask
;
1937 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1939 else if (PteContents
.u
.Hard
.Valid
== 1)
1942 // Get the PFN entry
1944 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
1947 // We don't support these yet
1949 ASSERT((NewProtect
& (PAGE_NOACCESS
| PAGE_GUARD
)) == 0);
1950 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
1953 // Write the protection mask and write it with a TLB flush
1955 Pfn1
->OriginalPte
.u
.Soft
.Protection
= ProtectionMask
;
1956 MiFlushTbAndCapture(FoundVad
,
1965 // We don't support these cases yet
1967 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
1968 ASSERT(PteContents
.u
.Soft
.Transition
== 0);
1971 // The PTE is already demand-zero, just update the protection mask
1973 PointerPte
->u
.Soft
.Protection
= ProtectionMask
;
1980 // Unlock the working set and update quota charges if needed, then return
1982 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1983 if ((QuotaCharge
> 0) && (!DontCharge
))
1985 FoundVad
->u
.VadFlags
.CommitCharge
-= QuotaCharge
;
1986 Process
->CommitCharge
-= QuotaCharge
;
1988 return STATUS_SUCCESS
;
1993 MiRemoveMappedPtes(IN PVOID BaseAddress
,
1994 IN ULONG NumberOfPtes
,
1995 IN PCONTROL_AREA ControlArea
,
1998 PMMPTE PointerPte
;//, FirstPte;
1999 PMMPDE PointerPde
, SystemMapPde
;
2003 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress
);
2005 /* Get the PTE and loop each one */
2006 PointerPte
= MiAddressToPte(BaseAddress
);
2007 //FirstPte = PointerPte;
2008 while (NumberOfPtes
)
2010 /* Check if the PTE is already valid */
2011 PteContents
= *PointerPte
;
2012 if (PteContents
.u
.Hard
.Valid
== 1)
2014 /* Get the PFN entry */
2015 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(&PteContents
));
2018 PointerPde
= MiAddressToPte(PointerPte
);
2020 /* Lock the PFN database and make sure this isn't a mapped file */
2021 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2022 ASSERT(((Pfn1
->u3
.e1
.PrototypePte
) && (Pfn1
->OriginalPte
.u
.Soft
.Prototype
)) == 0);
2024 /* FIXME: Dirty bit management */
2026 /* Was the PDE invalid */
2027 if (PointerPde
->u
.Long
== 0)
2029 #if (_MI_PAGING_LEVELS == 2)
2030 /* Find the system double-mapped PDE that describes this mapping */
2031 SystemMapPde
= &MmSystemPagePtes
[((ULONG_PTR
)PointerPde
& (SYSTEM_PD_SIZE
- 1)) / sizeof(MMPTE
)];
2034 ASSERT(SystemMapPde
->u
.Hard
.Valid
== 1);
2035 MI_WRITE_VALID_PDE(PointerPde
, *SystemMapPde
);
2041 /* Dereference the PDE and the PTE */
2042 Pfn2
= MiGetPfnEntry(PFN_FROM_PTE(PointerPde
));
2043 //MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2044 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2
);
2045 MiDecrementShareCount(Pfn1
, PFN_FROM_PTE(&PteContents
));
2047 /* Release the PFN lock */
2048 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2052 /* Windows ASSERT */
2053 ASSERT((PteContents
.u
.Long
== 0) || (PteContents
.u
.Soft
.Prototype
== 1));
2055 /* But not handled in ARM3 */
2056 ASSERT(PteContents
.u
.Soft
.Prototype
== 0);
2059 /* Make the PTE into a zero PTE */
2060 PointerPte
->u
.Long
= 0;
2062 /* Move to the next PTE */
2070 /* Acquire the PFN lock */
2071 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2073 /* Decrement the accounting counters */
2074 ControlArea
->NumberOfUserReferences
--;
2075 ControlArea
->NumberOfMappedViews
--;
2077 /* Check if we should destroy the CA and release the lock */
2078 MiCheckControlArea(ControlArea
, OldIrql
);
2083 MiRemoveFromSystemSpace(IN PMMSESSION Session
,
2085 OUT PCONTROL_AREA
*ControlArea
)
2087 ULONG Hash
, Size
, Count
= 0;
2091 /* Compute the hash for this entry and loop trying to find it */
2092 Entry
= (ULONG_PTR
)Base
>> 16;
2093 Hash
= Entry
% Session
->SystemSpaceHashKey
;
2094 while ((Session
->SystemSpaceViewTable
[Hash
].Entry
>> 16) != Entry
)
2096 /* Check if we overflew past the end of the hash table */
2097 if (++Hash
>= Session
->SystemSpaceHashSize
)
2099 /* Reset the hash to zero and keep searching from the bottom */
2103 /* But if we overflew twice, then this is not a real mapping */
2104 KeBugCheckEx(0xD7, //DRIVER_UNMAPPING_INVALID_VIEW,
2113 /* One less entry */
2114 Session
->SystemSpaceHashEntries
--;
2116 /* Extract the size and clear the entry */
2117 Size
= Session
->SystemSpaceViewTable
[Hash
].Entry
& 0xFFFF;
2118 Session
->SystemSpaceViewTable
[Hash
].Entry
= 0;
2120 /* Return the control area and the size */
2121 *ControlArea
= Session
->SystemSpaceViewTable
[Hash
].ControlArea
;
2127 MiUnmapViewInSystemSpace(IN PMMSESSION Session
,
2128 IN PVOID MappedBase
)
2131 PCONTROL_AREA ControlArea
;
2134 /* Only global mappings supported for now */
2135 ASSERT(Session
== &MmSession
);
2137 /* Remove this mapping */
2138 KeAcquireGuardedMutex(Session
->SystemSpaceViewLockPointer
);
2139 Size
= MiRemoveFromSystemSpace(Session
, MappedBase
, &ControlArea
);
2141 /* Clear the bits for this mapping */
2142 RtlClearBits(Session
->SystemSpaceBitMap
,
2143 (ULONG
)(((ULONG_PTR
)MappedBase
- (ULONG_PTR
)Session
->SystemSpaceViewStart
) >> 16),
2146 /* Convert the size from a bit size into the actual size */
2147 Size
= Size
* (_64K
>> PAGE_SHIFT
);
2149 /* Remove the PTEs now */
2150 MiRemoveMappedPtes(MappedBase
, Size
, ControlArea
, NULL
);
2151 KeReleaseGuardedMutex(Session
->SystemSpaceViewLockPointer
);
2153 /* Return success */
2154 return STATUS_SUCCESS
;
2157 /* PUBLIC FUNCTIONS ***********************************************************/
2164 MmCreateArm3Section(OUT PVOID
*SectionObject
,
2165 IN ACCESS_MASK DesiredAccess
,
2166 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
2167 IN PLARGE_INTEGER InputMaximumSize
,
2168 IN ULONG SectionPageProtection
,
2169 IN ULONG AllocationAttributes
,
2170 IN HANDLE FileHandle OPTIONAL
,
2171 IN PFILE_OBJECT FileObject OPTIONAL
)
2174 PSECTION NewSection
;
2175 PSUBSECTION Subsection
;
2176 PSEGMENT NewSegment
, Segment
;
2178 PCONTROL_AREA ControlArea
;
2179 ULONG ProtectionMask
, ControlAreaSize
, Size
, NonPagedCharge
, PagedCharge
;
2180 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2181 BOOLEAN FileLock
= FALSE
, KernelCall
= FALSE
;
2184 PVOID PreviousSectionPointer
;
2186 /* Make the same sanity checks that the Nt interface should've validated */
2187 ASSERT((AllocationAttributes
& ~(SEC_COMMIT
| SEC_RESERVE
| SEC_BASED
|
2188 SEC_LARGE_PAGES
| SEC_IMAGE
| SEC_NOCACHE
|
2189 SEC_NO_CHANGE
)) == 0);
2190 ASSERT((AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
| SEC_IMAGE
)) != 0);
2191 ASSERT(!((AllocationAttributes
& SEC_IMAGE
) &&
2192 (AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
|
2193 SEC_NOCACHE
| SEC_NO_CHANGE
))));
2194 ASSERT(!((AllocationAttributes
& SEC_COMMIT
) && (AllocationAttributes
& SEC_RESERVE
)));
2195 ASSERT(!((SectionPageProtection
& PAGE_NOCACHE
) ||
2196 (SectionPageProtection
& PAGE_WRITECOMBINE
) ||
2197 (SectionPageProtection
& PAGE_GUARD
) ||
2198 (SectionPageProtection
& PAGE_NOACCESS
)));
2200 /* Convert section flag to page flag */
2201 if (AllocationAttributes
& SEC_NOCACHE
) SectionPageProtection
|= PAGE_NOCACHE
;
2203 /* Check to make sure the protection is correct. Nt* does this already */
2204 ProtectionMask
= MiMakeProtectionMask(SectionPageProtection
);
2205 if (ProtectionMask
== MM_INVALID_PROTECTION
) return STATUS_INVALID_PAGE_PROTECTION
;
2207 /* Check if this is going to be a data or image backed file section */
2208 if ((FileHandle
) || (FileObject
))
2210 /* These cannot be mapped with large pages */
2211 if (AllocationAttributes
& SEC_LARGE_PAGES
) return STATUS_INVALID_PARAMETER_6
;
2213 /* For now, only support the mechanism through a file handle */
2214 ASSERT(FileObject
== NULL
);
2216 /* Reference the file handle to get the object */
2217 Status
= ObReferenceObjectByHandle(FileHandle
,
2218 MmMakeFileAccess
[ProtectionMask
],
2223 if (!NT_SUCCESS(Status
)) return Status
;
2225 /* Make sure Cc has been doing its job */
2226 if (!File
->SectionObjectPointer
)
2228 /* This is not a valid file system-based file, fail */
2229 ObDereferenceObject(File
);
2230 return STATUS_INVALID_FILE_FOR_SECTION
;
2233 /* Image-file backed sections are not yet supported */
2234 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2236 /* Compute the size of the control area, and allocate it */
2237 ControlAreaSize
= sizeof(CONTROL_AREA
) + sizeof(MSUBSECTION
);
2238 ControlArea
= ExAllocatePoolWithTag(NonPagedPool
, ControlAreaSize
, 'aCmM');
2241 ObDereferenceObject(File
);
2242 return STATUS_INSUFFICIENT_RESOURCES
;
2246 RtlZeroMemory(ControlArea
, ControlAreaSize
);
2248 /* Did we get a handle, or an object? */
2251 /* We got a file handle so we have to lock down the file */
2253 Status
= FsRtlAcquireToCreateMappedSection(File
, SectionPageProtection
);
2254 if (!NT_SUCCESS(Status
))
2256 ExFreePool(ControlArea
);
2257 ObDereferenceObject(File
);
2261 /* ReactOS doesn't support this API yet, so do nothing */
2262 Status
= STATUS_SUCCESS
;
2264 /* Update the top-level IRP so that drivers know what's happening */
2265 IoSetTopLevelIrp((PIRP
)FSRTL_FSP_TOP_LEVEL_IRP
);
2269 /* Lock the PFN database while we play with the section pointers */
2270 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2272 /* Image-file backed sections are not yet supported */
2273 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2275 /* There should not already be a control area for this file */
2276 ASSERT(File
->SectionObjectPointer
->DataSectionObject
== NULL
);
2279 /* Write down that this CA is being created, and set it */
2280 ControlArea
->u
.Flags
.BeingCreated
= TRUE
;
2281 PreviousSectionPointer
= File
->SectionObjectPointer
;
2282 File
->SectionObjectPointer
->DataSectionObject
= ControlArea
;
2284 /* We can release the PFN lock now */
2285 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2287 /* We don't support previously-mapped file */
2288 ASSERT(NewSegment
== NULL
);
2290 /* Image-file backed sections are not yet supported */
2291 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2293 /* So we always create a data file map */
2294 Status
= MiCreateDataFileMap(File
,
2296 (PSIZE_T
)InputMaximumSize
,
2297 SectionPageProtection
,
2298 AllocationAttributes
,
2300 ASSERT(PreviousSectionPointer
== File
->SectionObjectPointer
);
2301 ASSERT(NT_SUCCESS(Status
));
2303 /* Check if a maximum size was specified */
2304 if (!InputMaximumSize
->QuadPart
)
2306 /* Nope, use the segment size */
2307 Section
.SizeOfSection
.QuadPart
= (LONGLONG
)Segment
->SizeOfSegment
;
2311 /* Yep, use the entered size */
2312 Section
.SizeOfSection
.QuadPart
= InputMaximumSize
->QuadPart
;
2317 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2318 if (AllocationAttributes
& SEC_IMAGE
) return STATUS_INVALID_FILE_FOR_SECTION
;
2320 /* Not yet supported */
2321 ASSERT((AllocationAttributes
& SEC_LARGE_PAGES
) == 0);
2323 /* So this must be a pagefile-backed section, create the mappings needed */
2324 Status
= MiCreatePagingFileMap(&NewSegment
,
2325 (PSIZE_T
)InputMaximumSize
,
2327 AllocationAttributes
);
2328 if (!NT_SUCCESS(Status
)) return Status
;
2330 /* Set the size here, and read the control area */
2331 Section
.SizeOfSection
.QuadPart
= NewSegment
->SizeOfSegment
;
2332 ControlArea
= NewSegment
->ControlArea
;
2335 /* Did we already have a segment? */
2338 /* This must be the file path and we created a segment */
2339 NewSegment
= Segment
;
2340 ASSERT(File
!= NULL
);
2342 /* Acquire the PFN lock while we set control area flags */
2343 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
2345 /* We don't support this race condition yet, so assume no waiters */
2346 ASSERT(ControlArea
->WaitingForDeletion
== NULL
);
2347 ControlArea
->WaitingForDeletion
= NULL
;
2349 /* Image-file backed sections are not yet supported, nor ROM images */
2350 ASSERT((AllocationAttributes
& SEC_IMAGE
) == 0);
2351 ASSERT(Segment
->ControlArea
->u
.Flags
.Rom
== 0);
2353 /* Take off the being created flag, and then release the lock */
2354 ControlArea
->u
.Flags
.BeingCreated
= FALSE
;
2355 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
2358 /* Check if we locked the file earlier */
2361 /* Reset the top-level IRP and release the lock */
2362 IoSetTopLevelIrp(NULL
);
2363 //FsRtlReleaseFile(File);
2367 /* Set the initial section object data */
2368 Section
.InitialPageProtection
= SectionPageProtection
;
2370 /* The mapping created a control area and segment, save the flags */
2371 Section
.Segment
= NewSegment
;
2372 Section
.u
.LongFlags
= ControlArea
->u
.LongFlags
;
2374 /* Check if this is a user-mode read-write non-image file mapping */
2375 if (!(FileObject
) &&
2376 (SectionPageProtection
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)) &&
2377 (ControlArea
->u
.Flags
.Image
== 0) &&
2378 (ControlArea
->FilePointer
!= NULL
))
2380 /* Add a reference and set the flag */
2381 Section
.u
.Flags
.UserWritable
= 1;
2382 InterlockedIncrement((PLONG
)&ControlArea
->WritableUserReferences
);
2385 /* Check for image mappings or page file mappings */
2386 if ((ControlArea
->u
.Flags
.Image
== 1) || !(ControlArea
->FilePointer
))
2388 /* Charge the segment size, and allocate a subsection */
2389 PagedCharge
= sizeof(SECTION
) + NewSegment
->TotalNumberOfPtes
* sizeof(MMPTE
);
2390 Size
= sizeof(SUBSECTION
);
2394 /* Charge nothing, and allocate a mapped subsection */
2396 Size
= sizeof(MSUBSECTION
);
2399 /* Check if this is a normal CA */
2400 ASSERT(ControlArea
->u
.Flags
.GlobalOnlyPerSession
== 0);
2401 ASSERT(ControlArea
->u
.Flags
.Rom
== 0);
2403 /* Charge only a CA, and the subsection is right after */
2404 NonPagedCharge
= sizeof(CONTROL_AREA
);
2405 Subsection
= (PSUBSECTION
)(ControlArea
+ 1);
2407 /* We only support single-subsection mappings */
2408 NonPagedCharge
+= Size
;
2409 ASSERT(Subsection
->NextSubsection
== NULL
);
2411 /* Create the actual section object, with enough space for the prototype PTEs */
2412 Status
= ObCreateObject(PreviousMode
,
2413 MmSectionObjectType
,
2420 (PVOID
*)&NewSection
);
2421 ASSERT(NT_SUCCESS(Status
));
2423 /* Now copy the local section object from the stack into this new object */
2424 RtlCopyMemory(NewSection
, &Section
, sizeof(SECTION
));
2425 NewSection
->Address
.StartingVpn
= 0;
2427 /* For now, only user calls are supported */
2428 ASSERT(KernelCall
== FALSE
);
2429 NewSection
->u
.Flags
.UserReference
= TRUE
;
2431 /* Migrate the attribute into a flag */
2432 if (AllocationAttributes
& SEC_NO_CHANGE
) NewSection
->u
.Flags
.NoChange
= TRUE
;
2434 /* If R/W access is not requested, this might eventually become a CoW mapping */
2435 if (!(SectionPageProtection
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
2437 NewSection
->u
.Flags
.CopyOnWrite
= TRUE
;
2440 /* Is this a "based" allocation, in which all mappings are identical? */
2441 if (AllocationAttributes
& SEC_BASED
)
2443 /* Convert the flag, and make sure the section isn't too big */
2444 NewSection
->u
.Flags
.Based
= TRUE
;
2445 if ((ULONGLONG
)NewSection
->SizeOfSection
.QuadPart
>
2446 (ULONG_PTR
)MmHighSectionBase
)
2448 DPRINT1("BASED section is too large\n");
2449 ObDereferenceObject(NewSection
);
2450 return STATUS_NO_MEMORY
;
2453 /* Lock the VAD tree during the search */
2454 KeAcquireGuardedMutex(&MmSectionBasedMutex
);
2456 /* Find an address top-down */
2457 Status
= MiFindEmptyAddressRangeDownBasedTree(NewSection
->SizeOfSection
.LowPart
,
2458 (ULONG_PTR
)MmHighSectionBase
,
2460 &MmSectionBasedRoot
,
2461 &NewSection
->Address
.StartingVpn
);
2462 ASSERT(NT_SUCCESS(Status
));
2464 /* Compute the ending address and insert it into the VAD tree */
2465 NewSection
->Address
.EndingVpn
= NewSection
->Address
.StartingVpn
+
2466 NewSection
->SizeOfSection
.LowPart
-
2468 MiInsertBasedSection(NewSection
);
2470 /* Finally release the lock */
2471 KeReleaseGuardedMutex(&MmSectionBasedMutex
);
2474 /* Write down if this was a kernel call */
2475 ControlArea
->u
.Flags
.WasPurged
|= KernelCall
;
2476 ASSERT(ControlArea
->u
.Flags
.WasPurged
== FALSE
);
2478 /* Make sure the segment and the section are the same size, or the section is smaller */
2479 ASSERT((ULONG64
)NewSection
->SizeOfSection
.QuadPart
<= NewSection
->Segment
->SizeOfSegment
);
2481 /* Return the object and the creation status */
2482 *SectionObject
= (PVOID
)NewSection
;
2491 MmMapViewOfArm3Section(IN PVOID SectionObject
,
2492 IN PEPROCESS Process
,
2493 IN OUT PVOID
*BaseAddress
,
2494 IN ULONG_PTR ZeroBits
,
2495 IN SIZE_T CommitSize
,
2496 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL
,
2497 IN OUT PSIZE_T ViewSize
,
2498 IN SECTION_INHERIT InheritDisposition
,
2499 IN ULONG AllocationType
,
2502 KAPC_STATE ApcState
;
2503 BOOLEAN Attached
= FALSE
;
2505 PCONTROL_AREA ControlArea
;
2506 ULONG ProtectionMask
;
2510 /* Get the segment and control area */
2511 Section
= (PSECTION
)SectionObject
;
2512 ControlArea
= Section
->Segment
->ControlArea
;
2514 /* These flags/states are not yet supported by ARM3 */
2515 ASSERT(Section
->u
.Flags
.Image
== 0);
2516 ASSERT(Section
->u
.Flags
.NoCache
== 0);
2517 ASSERT(Section
->u
.Flags
.WriteCombined
== 0);
2518 ASSERT((AllocationType
& MEM_RESERVE
) == 0);
2519 ASSERT(ControlArea
->u
.Flags
.PhysicalMemory
== 0);
2521 /* Check if the mapping protection is compatible with the create */
2522 if (!MiIsProtectionCompatible(Section
->InitialPageProtection
, Protect
))
2524 DPRINT1("Mapping protection is incompatible\n");
2525 return STATUS_SECTION_PROTECTION
;
2528 /* Check if the offset and size would cause an overflow */
2529 if (((ULONG64
)SectionOffset
->QuadPart
+ *ViewSize
) <
2530 (ULONG64
)SectionOffset
->QuadPart
)
2532 DPRINT1("Section offset overflows\n");
2533 return STATUS_INVALID_VIEW_SIZE
;
2536 /* Check if the offset and size are bigger than the section itself */
2537 if (((ULONG64
)SectionOffset
->QuadPart
+ *ViewSize
) >
2538 (ULONG64
)Section
->SizeOfSection
.QuadPart
)
2540 DPRINT1("Section offset is larger than section\n");
2541 return STATUS_INVALID_VIEW_SIZE
;
2544 /* Check if the caller did not specify a view size */
2547 /* Compute it for the caller */
2548 *ViewSize
= (SIZE_T
)(Section
->SizeOfSection
.QuadPart
- SectionOffset
->QuadPart
);
2550 /* Check if it's larger than 4GB or overflows into kernel-mode */
2551 if ((*ViewSize
> 0xFFFFFFFF) ||
2552 (((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
- (ULONG_PTR
)*BaseAddress
) < *ViewSize
))
2554 DPRINT1("Section view won't fit\n");
2555 return STATUS_INVALID_VIEW_SIZE
;
2559 /* Check if the commit size is larger than the view size */
2560 if (CommitSize
> *ViewSize
)
2562 DPRINT1("Attempting to commit more than the view itself\n");
2563 return STATUS_INVALID_PARAMETER_5
;
2566 /* Check if the view size is larger than the section */
2567 if (*ViewSize
> (ULONG64
)Section
->SizeOfSection
.QuadPart
)
2569 DPRINT1("The view is larger than the section\n");
2570 return STATUS_INVALID_VIEW_SIZE
;
2573 /* Compute and validate the protection mask */
2574 ProtectionMask
= MiMakeProtectionMask(Protect
);
2575 if (ProtectionMask
== MM_INVALID_PROTECTION
)
2577 DPRINT1("The protection is invalid\n");
2578 return STATUS_INVALID_PAGE_PROTECTION
;
2581 /* We only handle pagefile-backed sections, which cannot be writecombined */
2582 if (Protect
& PAGE_WRITECOMBINE
)
2584 DPRINT1("Cannot write combine a pagefile-backed section\n");
2585 return STATUS_INVALID_PARAMETER_10
;
2588 /* Start by attaching to the current process if needed */
2589 if (PsGetCurrentProcess() != Process
)
2591 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
2595 /* Lock the address space and make sure the process is alive */
2596 MmLockAddressSpace(&Process
->Vm
);
2597 if (!Process
->VmDeleted
)
2599 /* Do the actual mapping */
2600 DPRINT("Mapping ARM3 data section\n");
2601 Status
= MiMapViewOfDataSection(ControlArea
,
2615 /* The process is being terminated, fail */
2616 DPRINT1("The process is dying\n");
2617 Status
= STATUS_PROCESS_IS_TERMINATING
;
2620 /* Unlock the address space and detatch if needed, then return status */
2621 MmUnlockAddressSpace(&Process
->Vm
);
2622 if (Attached
) KeUnstackDetachProcess(&ApcState
);
2631 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
)
2642 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
,
2643 IN BOOLEAN DelayClose
)
2654 MmMapViewInSessionSpace(IN PVOID Section
,
2655 OUT PVOID
*MappedBase
,
2656 IN OUT PSIZE_T ViewSize
)
2660 /* Process must be in a session */
2661 if (PsGetCurrentProcess()->ProcessInSession
== FALSE
)
2663 DPRINT1("Process is not in session\n");
2664 return STATUS_NOT_MAPPED_VIEW
;
2667 /* Use the system space API, but with the session view instead */
2668 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
2669 return MiMapViewInSystemSpace(Section
,
2670 &MmSessionSpace
->Session
,
2680 MmUnmapViewInSessionSpace(IN PVOID MappedBase
)
2684 /* Process must be in a session */
2685 if (PsGetCurrentProcess()->ProcessInSession
== FALSE
)
2687 DPRINT1("Proess is not in session\n");
2688 return STATUS_NOT_MAPPED_VIEW
;
2691 /* Use the system space API, but with the session view instead */
2692 ASSERT(MmIsAddressValid(MmSessionSpace
) == TRUE
);
2693 return MiUnmapViewInSystemSpace(&MmSessionSpace
->Session
,
2702 MmUnmapViewOfSection(IN PEPROCESS Process
,
2703 IN PVOID BaseAddress
)
2705 return MiUnmapViewOfSection(Process
, BaseAddress
, 0);
2713 MmUnmapViewInSystemSpace(IN PVOID MappedBase
)
2715 PMEMORY_AREA MemoryArea
;
2718 /* Was this mapped by RosMm? */
2719 MemoryArea
= MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase
);
2720 if ((MemoryArea
) && (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
2722 return MiRosUnmapViewInSystemSpace(MappedBase
);
2725 /* It was not, call the ARM3 routine */
2726 return MiUnmapViewInSystemSpace(&MmSession
, MappedBase
);
2729 /* SYSTEM CALLS ***************************************************************/
2733 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage
,
2734 IN PVOID File2MappedAsFile
)
2737 PMEMORY_AREA MemoryArea1
, MemoryArea2
;
2738 PROS_SECTION_OBJECT Section1
, Section2
;
2740 /* Lock address space */
2741 AddressSpace
= MmGetCurrentAddressSpace();
2742 MmLockAddressSpace(AddressSpace
);
2744 /* Locate the memory area for the process by address */
2745 MemoryArea1
= MmLocateMemoryAreaByAddress(AddressSpace
, File1MappedAsAnImage
);
2748 /* Fail, the address does not exist */
2749 MmUnlockAddressSpace(AddressSpace
);
2750 return STATUS_INVALID_ADDRESS
;
2753 /* Check if it's a section view (RosMm section) or ARM3 section */
2754 if (MemoryArea1
->Type
!= MEMORY_AREA_SECTION_VIEW
)
2756 /* Fail, the address is not a section */
2757 MmUnlockAddressSpace(AddressSpace
);
2758 return STATUS_CONFLICTING_ADDRESSES
;
2761 /* Get the section pointer to the SECTION_OBJECT */
2762 Section1
= MemoryArea1
->Data
.SectionData
.Section
;
2763 if (Section1
->FileObject
== NULL
)
2765 MmUnlockAddressSpace(AddressSpace
);
2766 return STATUS_CONFLICTING_ADDRESSES
;
2769 /* Locate the memory area for the process by address */
2770 MemoryArea2
= MmLocateMemoryAreaByAddress(AddressSpace
, File2MappedAsFile
);
2773 /* Fail, the address does not exist */
2774 MmUnlockAddressSpace(AddressSpace
);
2775 return STATUS_INVALID_ADDRESS
;
2778 /* Check if it's a section view (RosMm section) or ARM3 section */
2779 if (MemoryArea2
->Type
!= MEMORY_AREA_SECTION_VIEW
)
2781 /* Fail, the address is not a section */
2782 MmUnlockAddressSpace(AddressSpace
);
2783 return STATUS_CONFLICTING_ADDRESSES
;
2786 /* Get the section pointer to the SECTION_OBJECT */
2787 Section2
= MemoryArea2
->Data
.SectionData
.Section
;
2788 if (Section2
->FileObject
== NULL
)
2790 MmUnlockAddressSpace(AddressSpace
);
2791 return STATUS_CONFLICTING_ADDRESSES
;
2794 /* The shared cache map seems to be the same if both of these are equal */
2795 if (Section1
->FileObject
->SectionObjectPointer
->SharedCacheMap
==
2796 Section2
->FileObject
->SectionObjectPointer
->SharedCacheMap
)
2798 MmUnlockAddressSpace(AddressSpace
);
2799 return STATUS_SUCCESS
;
2802 /* Unlock address space */
2803 MmUnlockAddressSpace(AddressSpace
);
2804 return STATUS_NOT_SAME_DEVICE
;
2812 NtCreateSection(OUT PHANDLE SectionHandle
,
2813 IN ACCESS_MASK DesiredAccess
,
2814 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
2815 IN PLARGE_INTEGER MaximumSize OPTIONAL
,
2816 IN ULONG SectionPageProtection OPTIONAL
,
2817 IN ULONG AllocationAttributes
,
2818 IN HANDLE FileHandle OPTIONAL
)
2820 LARGE_INTEGER SafeMaximumSize
;
2821 PVOID SectionObject
;
2823 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2827 /* Check for non-existing flags */
2828 if ((AllocationAttributes
& ~(SEC_COMMIT
| SEC_RESERVE
| SEC_BASED
|
2829 SEC_LARGE_PAGES
| SEC_IMAGE
| SEC_NOCACHE
|
2832 if (!(AllocationAttributes
& 1))
2834 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes
);
2835 return STATUS_INVALID_PARAMETER_6
;
2839 /* Check for no allocation type */
2840 if (!(AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
| SEC_IMAGE
)))
2842 DPRINT1("Missing allocation type in allocation attributes\n");
2843 return STATUS_INVALID_PARAMETER_6
;
2846 /* Check for image allocation with invalid attributes */
2847 if ((AllocationAttributes
& SEC_IMAGE
) &&
2848 (AllocationAttributes
& (SEC_COMMIT
| SEC_RESERVE
| SEC_LARGE_PAGES
|
2849 SEC_NOCACHE
| SEC_NO_CHANGE
)))
2851 DPRINT1("Image allocation with invalid attributes\n");
2852 return STATUS_INVALID_PARAMETER_6
;
2855 /* Check for allocation type is both commit and reserve */
2856 if ((AllocationAttributes
& SEC_COMMIT
) && (AllocationAttributes
& SEC_RESERVE
))
2858 DPRINT1("Commit and reserve in the same time\n");
2859 return STATUS_INVALID_PARAMETER_6
;
2862 /* Now check for valid protection */
2863 if ((SectionPageProtection
& PAGE_NOCACHE
) ||
2864 (SectionPageProtection
& PAGE_WRITECOMBINE
) ||
2865 (SectionPageProtection
& PAGE_GUARD
) ||
2866 (SectionPageProtection
& PAGE_NOACCESS
))
2868 DPRINT1("Sections don't support these protections\n");
2869 return STATUS_INVALID_PAGE_PROTECTION
;
2872 /* Use a maximum size of zero, if none was specified */
2873 SafeMaximumSize
.QuadPart
= 0;
2875 /* Check for user-mode caller */
2876 if (PreviousMode
!= KernelMode
)
2881 /* Safely check user-mode parameters */
2882 if (MaximumSize
) SafeMaximumSize
= ProbeForReadLargeInteger(MaximumSize
);
2883 MaximumSize
= &SafeMaximumSize
;
2884 ProbeForWriteHandle(SectionHandle
);
2886 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2888 /* Return the exception code */
2889 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2893 else if (!MaximumSize
) MaximumSize
= &SafeMaximumSize
;
2895 /* Check that MaximumSize is valid if backed by paging file */
2896 if ((!FileHandle
) && (!MaximumSize
->QuadPart
))
2897 return STATUS_INVALID_PARAMETER_4
;
2899 /* Create the section */
2900 Status
= MmCreateSection(&SectionObject
,
2904 SectionPageProtection
,
2905 AllocationAttributes
,
2908 if (!NT_SUCCESS(Status
)) return Status
;
2910 /* FIXME: Should zero last page for a file mapping */
2912 /* Now insert the object */
2913 Status
= ObInsertObject(SectionObject
,
2919 if (NT_SUCCESS(Status
))
2924 /* Return the handle safely */
2925 *SectionHandle
= Handle
;
2927 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2934 /* Return the status */
2940 NtOpenSection(OUT PHANDLE SectionHandle
,
2941 IN ACCESS_MASK DesiredAccess
,
2942 IN POBJECT_ATTRIBUTES ObjectAttributes
)
2946 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
2949 /* Check for user-mode caller */
2950 if (PreviousMode
!= KernelMode
)
2955 /* Safely check user-mode parameters */
2956 ProbeForWriteHandle(SectionHandle
);
2958 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2960 /* Return the exception code */
2961 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2966 /* Try opening the object */
2967 Status
= ObOpenObjectByName(ObjectAttributes
,
2968 MmSectionObjectType
,
2978 /* Return the handle safely */
2979 *SectionHandle
= Handle
;
2981 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2987 /* Return the status */
2993 NtMapViewOfSection(IN HANDLE SectionHandle
,
2994 IN HANDLE ProcessHandle
,
2995 IN OUT PVOID
* BaseAddress
,
2996 IN ULONG_PTR ZeroBits
,
2997 IN SIZE_T CommitSize
,
2998 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL
,
2999 IN OUT PSIZE_T ViewSize
,
3000 IN SECTION_INHERIT InheritDisposition
,
3001 IN ULONG AllocationType
,
3004 PVOID SafeBaseAddress
;
3005 LARGE_INTEGER SafeSectionOffset
;
3006 SIZE_T SafeViewSize
;
3007 PROS_SECTION_OBJECT Section
;
3010 ACCESS_MASK DesiredAccess
;
3011 ULONG ProtectionMask
;
3012 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3014 /* Check for invalid zero bits */
3015 if (ZeroBits
> 21) // per-arch?
3017 DPRINT1("Invalid zero bits\n");
3018 return STATUS_INVALID_PARAMETER_4
;
3021 /* Check for invalid inherit disposition */
3022 if ((InheritDisposition
> ViewUnmap
) || (InheritDisposition
< ViewShare
))
3024 DPRINT1("Invalid inherit disposition\n");
3025 return STATUS_INVALID_PARAMETER_8
;
3028 /* Allow only valid allocation types */
3029 if ((AllocationType
& ~(MEM_TOP_DOWN
| MEM_LARGE_PAGES
| MEM_DOS_LIM
|
3030 SEC_NO_CHANGE
| MEM_RESERVE
)))
3032 DPRINT1("Invalid allocation type\n");
3033 return STATUS_INVALID_PARAMETER_9
;
3036 /* Convert the protection mask, and validate it */
3037 ProtectionMask
= MiMakeProtectionMask(Protect
);
3038 if (ProtectionMask
== MM_INVALID_PROTECTION
)
3040 DPRINT1("Invalid page protection\n");
3041 return STATUS_INVALID_PAGE_PROTECTION
;
3044 /* Check for non-allocation-granularity-aligned BaseAddress */
3045 if (BaseAddress
&& (*BaseAddress
!= ALIGN_DOWN_POINTER_BY(*BaseAddress
, MM_VIRTMEM_GRANULARITY
)))
3047 DPRINT("BaseAddress is not at 64-kilobyte address boundary.");
3048 return STATUS_MAPPED_ALIGNMENT
;
3051 /* Now convert the protection mask into desired section access mask */
3052 DesiredAccess
= MmMakeSectionAccess
[ProtectionMask
& 0x7];
3054 /* Assume no section offset */
3055 SafeSectionOffset
.QuadPart
= 0;
3060 /* Check for unsafe parameters */
3061 if (PreviousMode
!= KernelMode
)
3063 /* Probe the parameters */
3064 ProbeForWritePointer(BaseAddress
);
3065 ProbeForWriteSize_t(ViewSize
);
3068 /* Check if a section offset was given */
3071 /* Check for unsafe parameters and capture section offset */
3072 if (PreviousMode
!= KernelMode
) ProbeForWriteLargeInteger(SectionOffset
);
3073 SafeSectionOffset
= *SectionOffset
;
3076 /* Capture the other parameters */
3077 SafeBaseAddress
= *BaseAddress
;
3078 SafeViewSize
= *ViewSize
;
3080 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3082 /* Return the exception code */
3083 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3087 /* Check for kernel-mode address */
3088 if (SafeBaseAddress
> MM_HIGHEST_VAD_ADDRESS
)
3090 DPRINT1("Kernel base not allowed\n");
3091 return STATUS_INVALID_PARAMETER_3
;
3094 /* Check for range entering kernel-mode */
3095 if (((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
- (ULONG_PTR
)SafeBaseAddress
) < SafeViewSize
)
3097 DPRINT1("Overflowing into kernel base not allowed\n");
3098 return STATUS_INVALID_PARAMETER_3
;
3101 /* Check for invalid zero bits */
3102 if (((ULONG_PTR
)SafeBaseAddress
+ SafeViewSize
) > (0xFFFFFFFF >> ZeroBits
)) // arch?
3104 DPRINT1("Invalid zero bits\n");
3105 return STATUS_INVALID_PARAMETER_4
;
3108 /* Reference the process */
3109 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3110 PROCESS_VM_OPERATION
,
3115 if (!NT_SUCCESS(Status
)) return Status
;
3117 /* Reference the section */
3118 Status
= ObReferenceObjectByHandle(SectionHandle
,
3120 MmSectionObjectType
,
3124 if (!NT_SUCCESS(Status
))
3126 ObDereferenceObject(Process
);
3130 /* Now do the actual mapping */
3131 Status
= MmMapViewOfSection(Section
,
3142 /* Return data only on success */
3143 if (NT_SUCCESS(Status
))
3145 /* Check if this is an image for the current process */
3146 if ((Section
->AllocationAttributes
& SEC_IMAGE
) &&
3147 (Process
== PsGetCurrentProcess()) &&
3148 (Status
!= STATUS_IMAGE_NOT_AT_BASE
))
3150 /* Notify the debugger */
3151 DbgkMapViewOfSection(Section
,
3153 SafeSectionOffset
.LowPart
,
3160 /* Return parameters to user */
3161 *BaseAddress
= SafeBaseAddress
;
3162 *ViewSize
= SafeViewSize
;
3163 if (SectionOffset
) *SectionOffset
= SafeSectionOffset
;
3165 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3172 /* Dereference all objects and return status */
3173 ObDereferenceObject(Section
);
3174 ObDereferenceObject(Process
);
3180 NtUnmapViewOfSection(IN HANDLE ProcessHandle
,
3181 IN PVOID BaseAddress
)
3185 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3187 /* Don't allowing mapping kernel views */
3188 if ((PreviousMode
== UserMode
) && (BaseAddress
> MM_HIGHEST_USER_ADDRESS
))
3190 DPRINT1("Trying to unmap a kernel view\n");
3191 return STATUS_NOT_MAPPED_VIEW
;
3194 /* Reference the process */
3195 Status
= ObReferenceObjectByHandle(ProcessHandle
,
3196 PROCESS_VM_OPERATION
,
3201 if (!NT_SUCCESS(Status
)) return Status
;
3203 /* Unmap the view */
3204 Status
= MiUnmapViewOfSection(Process
, BaseAddress
, 0);
3206 /* Dereference the process and return status */
3207 ObDereferenceObject(Process
);
3213 NtExtendSection(IN HANDLE SectionHandle
,
3214 IN OUT PLARGE_INTEGER NewMaximumSize
)
3216 LARGE_INTEGER SafeNewMaximumSize
;
3217 PROS_SECTION_OBJECT Section
;
3219 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
3221 /* Check for user-mode parameters */
3222 if (PreviousMode
!= KernelMode
)
3227 /* Probe and capture the maximum size, it's both read and write */
3228 ProbeForWriteLargeInteger(NewMaximumSize
);
3229 SafeNewMaximumSize
= *NewMaximumSize
;
3231 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3233 /* Return the exception code */
3234 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3240 /* Just read the size directly */
3241 SafeNewMaximumSize
= *NewMaximumSize
;
3244 /* Reference the section */
3245 Status
= ObReferenceObjectByHandle(SectionHandle
,
3246 SECTION_EXTEND_SIZE
,
3247 MmSectionObjectType
,
3251 if (!NT_SUCCESS(Status
)) return Status
;
3253 /* Really this should go in MmExtendSection */
3254 if (!(Section
->AllocationAttributes
& SEC_FILE
))
3256 DPRINT1("Not extending a file\n");
3257 ObDereferenceObject(Section
);
3258 return STATUS_SECTION_NOT_EXTENDED
;
3261 /* FIXME: Do the work */
3263 /* Dereference the section */
3264 ObDereferenceObject(Section
);
3269 /* Write back the new size */
3270 *NewMaximumSize
= SafeNewMaximumSize
;
3272 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
3278 /* Return the status */
3279 return STATUS_NOT_IMPLEMENTED
;