2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/marea.c
21 * PURPOSE: Implements memory areas
23 * PROGRAMMERS: Rex Jolliff
33 * Thomas Weidenmueller
34 * Gunnar Andre' Dalsnes
42 /* INCLUDES *****************************************************************/
46 #include "../cache/section/newmm.h"
49 #include "ARM3/miarm.h"
51 MEMORY_AREA MiStaticMemoryAreas
[MI_STATIC_MEMORY_AREAS
];
52 ULONG MiStaticMemoryAreaCount
;
54 /* FUNCTIONS *****************************************************************/
57 * @name MmIterateFirstNode
60 * Head node of the MEMORY_AREA tree.
62 * @return The leftmost MEMORY_AREA node (ie. the one with lowest
66 static PMEMORY_AREA
MmIterateFirstNode(PMEMORY_AREA Node
)
68 while (Node
->LeftChild
!= NULL
)
69 Node
= Node
->LeftChild
;
75 * @name MmIterateNextNode
78 * Current node in the tree.
80 * @return Next node in the tree (sorted by address).
83 static PMEMORY_AREA
MmIterateNextNode(PMEMORY_AREA Node
)
85 if (Node
->RightChild
!= NULL
)
87 Node
= Node
->RightChild
;
88 while (Node
->LeftChild
!= NULL
)
89 Node
= Node
->LeftChild
;
93 PMEMORY_AREA TempNode
= NULL
;
97 /* Check if we're at the end of tree. */
98 if (Node
->Parent
== NULL
)
104 while (TempNode
== Node
->RightChild
);
110 * @name MmIterateLastNode
113 * Head node of the MEMORY_AREA tree.
115 * @return The rightmost MEMORY_AREA node (ie. the one with highest
119 static PMEMORY_AREA
MmIterateLastNode(PMEMORY_AREA Node
)
121 while (Node
->RightChild
!= NULL
)
122 Node
= Node
->RightChild
;
128 * @name MmIteratePreviousNode
131 * Current node in the tree.
133 * @return Previous node in the tree (sorted by address).
136 static PMEMORY_AREA
MmIteratePrevNode(PMEMORY_AREA Node
)
138 if (Node
->LeftChild
!= NULL
)
140 Node
= Node
->LeftChild
;
141 while (Node
->RightChild
!= NULL
)
142 Node
= Node
->RightChild
;
146 PMEMORY_AREA TempNode
= NULL
;
150 /* Check if we're at the end of tree. */
151 if (Node
->Parent
== NULL
)
157 while (TempNode
== Node
->LeftChild
);
163 MmLocateMemoryAreaByAddress(
164 PMMSUPPORT AddressSpace
,
167 PMEMORY_AREA Node
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
169 DPRINT("MmLocateMemoryAreaByAddress(AddressSpace %p, Address %p)\n",
170 AddressSpace
, Address
);
174 if (Address
< Node
->StartingAddress
)
175 Node
= Node
->LeftChild
;
176 else if (Address
>= Node
->EndingAddress
)
177 Node
= Node
->RightChild
;
180 DPRINT("MmLocateMemoryAreaByAddress(%p): %p [%p - %p]\n",
181 Address
, Node
, Node
->StartingAddress
, Node
->EndingAddress
);
186 DPRINT("MmLocateMemoryAreaByAddress(%p): 0\n", Address
);
191 MmLocateMemoryAreaByRegion(
192 PMMSUPPORT AddressSpace
,
197 PVOID Extent
= (PVOID
)((ULONG_PTR
)Address
+ Length
);
199 /* Special case for empty tree. */
200 if (AddressSpace
->WorkingSetExpansionLinks
.Flink
== NULL
)
203 /* Traverse the tree from left to right. */
204 for (Node
= MmIterateFirstNode((PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
);
206 Node
= MmIterateNextNode(Node
))
208 if (Node
->StartingAddress
>= Address
&&
209 Node
->StartingAddress
< Extent
)
211 DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
212 Address
, (ULONG_PTR
)Address
+ Length
, Node
->StartingAddress
,
213 Node
->EndingAddress
);
216 if (Node
->EndingAddress
> Address
&&
217 Node
->EndingAddress
< Extent
)
219 DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
220 Address
, (ULONG_PTR
)Address
+ Length
, Node
->StartingAddress
,
221 Node
->EndingAddress
);
224 if (Node
->StartingAddress
<= Address
&&
225 Node
->EndingAddress
>= Extent
)
227 DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
228 Address
, (ULONG_PTR
)Address
+ Length
, Node
->StartingAddress
,
229 Node
->EndingAddress
);
232 if (Node
->StartingAddress
>= Extent
)
234 DPRINT("Finished MmLocateMemoryAreaByRegion() = NULL\n");
243 * @name MmCompressHelper
245 * This is helper of MmRebalanceTree. Performs a compression transformation
246 * count times, starting at root.
251 PMMSUPPORT AddressSpace
,
254 PMEMORY_AREA Root
= NULL
;
255 PMEMORY_AREA Red
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
256 PMEMORY_AREA Black
= Red
->LeftChild
;
261 Root
->LeftChild
= Black
;
263 AddressSpace
->WorkingSetExpansionLinks
.Flink
= (PVOID
)Black
;
264 Black
->Parent
= Root
;
265 Red
->LeftChild
= Black
->RightChild
;
266 if (Black
->RightChild
)
267 Black
->RightChild
->Parent
= Red
;
268 Black
->RightChild
= Red
;
274 Red
= Root
->LeftChild
;
275 Black
= Red
->LeftChild
;
281 * @name MmRebalanceTree
283 * Rebalance a memory area tree using the Tree->Vine->Balanced Tree
284 * method described in libavl documentation in chapter 4.12.
285 * (http://www.stanford.edu/~blp/avl/libavl.html/)
290 PMMSUPPORT AddressSpace
)
292 PMEMORY_AREA PreviousNode
;
293 PMEMORY_AREA CurrentNode
;
294 PMEMORY_AREA TempNode
;
296 ULONG Vine
; /* Number of nodes in main vine. */
297 ULONG Leaves
; /* Nodes in incomplete bottom level, if any. */
298 INT Height
; /* Height of produced balanced tree. */
300 /* Transform the tree into Vine. */
303 CurrentNode
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
304 while (CurrentNode
!= NULL
)
306 if (CurrentNode
->RightChild
== NULL
)
308 PreviousNode
= CurrentNode
;
309 CurrentNode
= CurrentNode
->LeftChild
;
314 TempNode
= CurrentNode
->RightChild
;
316 CurrentNode
->RightChild
= TempNode
->LeftChild
;
317 if (TempNode
->LeftChild
)
318 TempNode
->LeftChild
->Parent
= CurrentNode
;
320 TempNode
->LeftChild
= CurrentNode
;
321 CurrentNode
->Parent
= TempNode
;
323 CurrentNode
= TempNode
;
325 if (PreviousNode
!= NULL
)
326 PreviousNode
->LeftChild
= TempNode
;
328 AddressSpace
->WorkingSetExpansionLinks
.Flink
= (PVOID
)TempNode
;
329 TempNode
->Parent
= PreviousNode
;
333 /* Transform Vine back into a balanced tree. */
335 Leaves
= NodeCount
+ 1;
338 ULONG Next
= Leaves
& (Leaves
- 1);
343 Leaves
= NodeCount
+ 1 - Leaves
;
345 MmCompressHelper(AddressSpace
, Leaves
);
347 Vine
= NodeCount
- Leaves
;
348 Height
= 1 + (Leaves
> 0);
351 MmCompressHelper(AddressSpace
, Vine
/ 2);
359 MiInsertVad(IN PMMVAD Vad
,
360 IN PEPROCESS Process
);
364 MiMakeProtectionMask(
370 PMMSUPPORT AddressSpace
,
374 PMEMORY_AREA PreviousNode
;
376 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
378 /* Build a lame VAD if this is a user-space allocation */
379 if ((marea
->EndingAddress
< MmSystemRangeStart
) && (marea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
))
383 ASSERT(marea
->Type
== MEMORY_AREA_SECTION_VIEW
|| marea
->Type
== MEMORY_AREA_CACHE
);
384 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD
), TAG_MVAD
);
386 RtlZeroMemory(Vad
, sizeof(MMVAD
));
387 Vad
->StartingVpn
= PAGE_ROUND_DOWN(marea
->StartingAddress
) >> PAGE_SHIFT
;
389 * For some strange reason, it is perfectly valid to create a MAREA from 0x1000 to... 0x1000.
390 * In a normal OS/Memory Manager, this would be retarded, but ReactOS allows this (how it works
391 * I don't even want to know).
393 if (marea
->EndingAddress
!= marea
->StartingAddress
)
395 Vad
->EndingVpn
= PAGE_ROUND_DOWN((ULONG_PTR
)marea
->EndingAddress
- 1) >> PAGE_SHIFT
;
399 Vad
->EndingVpn
= Vad
->StartingVpn
;
401 Vad
->u
.VadFlags
.Spare
= 1;
402 Vad
->u
.VadFlags
.PrivateMemory
= 1;
403 Vad
->u
.VadFlags
.Protection
= MiMakeProtectionMask(marea
->Protect
);
406 MiInsertVad(Vad
, Process
);
414 if (AddressSpace
->WorkingSetExpansionLinks
.Flink
== NULL
)
416 AddressSpace
->WorkingSetExpansionLinks
.Flink
= (PVOID
)marea
;
417 marea
->LeftChild
= marea
->RightChild
= marea
->Parent
= NULL
;
421 Node
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
424 DPRINT("marea->EndingAddress: %p Node->StartingAddress: %p\n",
425 marea
->EndingAddress
, Node
->StartingAddress
);
426 DPRINT("marea->StartingAddress: %p Node->EndingAddress: %p\n",
427 marea
->StartingAddress
, Node
->EndingAddress
);
428 ASSERT(marea
->EndingAddress
<= Node
->StartingAddress
||
429 marea
->StartingAddress
>= Node
->EndingAddress
);
430 ASSERT(marea
->StartingAddress
!= Node
->StartingAddress
);
434 if (marea
->StartingAddress
< Node
->StartingAddress
)
435 Node
= Node
->LeftChild
;
437 Node
= Node
->RightChild
;
444 MmRebalanceTree(AddressSpace
);
445 PreviousNode
= Node
->Parent
;
449 while (Node
!= NULL
);
451 marea
->LeftChild
= marea
->RightChild
= NULL
;
452 marea
->Parent
= PreviousNode
;
453 if (marea
->StartingAddress
< PreviousNode
->StartingAddress
)
454 PreviousNode
->LeftChild
= marea
;
456 PreviousNode
->RightChild
= marea
;
461 PMMSUPPORT AddressSpace
,
463 ULONG_PTR Granularity
)
465 ULONG_PTR LowestAddress
, HighestAddress
, Candidate
;
466 PMEMORY_AREA Root
, Node
;
468 /* Get the margins of the address space */
469 if (MmGetAddressSpaceOwner(AddressSpace
) != NULL
)
471 LowestAddress
= (ULONG_PTR
)MM_LOWEST_USER_ADDRESS
;
472 HighestAddress
= (ULONG_PTR
)MmHighestUserAddress
;
476 LowestAddress
= (ULONG_PTR
)MmSystemRangeStart
;
477 HighestAddress
= MAXULONG_PTR
;
480 /* Start with the lowest address */
481 Candidate
= LowestAddress
;
483 /* Check for overflow */
484 if ((Candidate
+ Length
) < Candidate
) return NULL
;
486 /* Get the root of the address space tree */
487 Root
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
489 /* Go to the node with lowest address in the tree. */
490 Node
= Root
? MmIterateFirstNode(Root
) : NULL
;
491 while (Node
&& ((ULONG_PTR
)Node
->EndingAddress
< LowestAddress
))
493 Node
= MmIterateNextNode(Node
);
496 /* Traverse the tree from low to high addresses */
497 while (Node
&& ((ULONG_PTR
)Node
->EndingAddress
< HighestAddress
))
499 /* Check if the memory area fits before the current node */
500 if ((ULONG_PTR
)Node
->StartingAddress
>= (Candidate
+ Length
))
502 DPRINT("MmFindGapBottomUp: %p\n", Candidate
);
503 ASSERT(Candidate
>= LowestAddress
);
504 return (PVOID
)Candidate
;
507 /* Calculate next possible adress above this node */
508 Candidate
= ALIGN_UP_BY((ULONG_PTR
)Node
->EndingAddress
, Granularity
);
510 /* Check for overflow */
511 if ((Candidate
+ Length
) < (ULONG_PTR
)Node
->EndingAddress
) return NULL
;
513 /* Go to the next higher node */
514 Node
= MmIterateNextNode(Node
);
517 /* Check if there is enough space after the last memory area. */
518 if ((Candidate
+ Length
) <= HighestAddress
)
520 DPRINT("MmFindGapBottomUp: %p\n", Candidate
);
521 ASSERT(Candidate
>= LowestAddress
);
522 return (PVOID
)Candidate
;
525 DPRINT("MmFindGapBottomUp: 0\n");
532 PMMSUPPORT AddressSpace
,
534 ULONG_PTR Granularity
)
536 ULONG_PTR LowestAddress
, HighestAddress
, Candidate
;
537 PMEMORY_AREA Root
, Node
;
539 /* Get the margins of the address space */
540 if (MmGetAddressSpaceOwner(AddressSpace
) != NULL
)
542 LowestAddress
= (ULONG_PTR
)MM_LOWEST_USER_ADDRESS
;
543 HighestAddress
= (ULONG_PTR
)MmHighestUserAddress
;
547 LowestAddress
= (ULONG_PTR
)MmSystemRangeStart
;
548 HighestAddress
= MAXULONG_PTR
;
551 /* Calculate the highest candidate */
552 Candidate
= ALIGN_DOWN_BY(HighestAddress
+ 1 - Length
, Granularity
);
554 /* Check for overflow. */
555 if (Candidate
> HighestAddress
) return NULL
;
557 /* Get the root of the address space tree */
558 Root
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
560 /* Go to the node with highest address in the tree. */
561 Node
= Root
? MmIterateLastNode(Root
) : NULL
;
562 while (Node
&& ((ULONG_PTR
)Node
->StartingAddress
> HighestAddress
))
564 Node
= MmIteratePrevNode(Node
);
567 /* Traverse the tree from high to low addresses */
568 while (Node
&& ((ULONG_PTR
)Node
->StartingAddress
> LowestAddress
))
570 /* Check if the memory area fits after the current node */
571 if ((ULONG_PTR
)Node
->EndingAddress
<= Candidate
)
573 DPRINT("MmFindGapTopDown: %p\n", Candidate
);
574 return (PVOID
)Candidate
;
577 /* Calculate next possible adress below this node */
578 Candidate
= ALIGN_DOWN_BY((ULONG_PTR
)Node
->StartingAddress
- Length
,
581 /* Check for overflow. */
582 if (Candidate
> (ULONG_PTR
)Node
->StartingAddress
)
585 /* Go to the next lower node */
586 Node
= MmIteratePrevNode(Node
);
589 /* Check if the last candidate is inside the given range */
590 if (Candidate
>= LowestAddress
)
592 DPRINT("MmFindGapTopDown: %p\n", Candidate
);
593 return (PVOID
)Candidate
;
596 DPRINT("MmFindGapTopDown: 0\n");
603 PMMSUPPORT AddressSpace
,
605 ULONG_PTR Granularity
,
609 return MmFindGapTopDown(AddressSpace
, Length
, Granularity
);
611 return MmFindGapBottomUp(AddressSpace
, Length
, Granularity
);
616 PMMSUPPORT AddressSpace
,
619 PMEMORY_AREA Node
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
620 PMEMORY_AREA RightNeighbour
= NULL
;
621 PVOID LowestAddress
= MmGetAddressSpaceOwner(AddressSpace
) ? MM_LOWEST_USER_ADDRESS
: MmSystemRangeStart
;
622 PVOID HighestAddress
= MmGetAddressSpaceOwner(AddressSpace
) ?
623 (PVOID
)((ULONG_PTR
)MmSystemRangeStart
- 1) : (PVOID
)MAXULONG_PTR
;
625 Address
= MM_ROUND_DOWN(Address
, PAGE_SIZE
);
627 if (LowestAddress
< MmSystemRangeStart
)
629 if (Address
>= MmSystemRangeStart
)
636 if (Address
< LowestAddress
)
644 if (Address
< Node
->StartingAddress
)
646 RightNeighbour
= Node
;
647 Node
= Node
->LeftChild
;
649 else if (Address
>= Node
->EndingAddress
)
651 Node
= Node
->RightChild
;
655 DPRINT("MmFindGapAtAddress: 0\n");
662 DPRINT("MmFindGapAtAddress: %p [%p]\n", Address
,
663 (ULONG_PTR
)RightNeighbour
->StartingAddress
- (ULONG_PTR
)Address
);
664 return (ULONG_PTR
)RightNeighbour
->StartingAddress
- (ULONG_PTR
)Address
;
668 DPRINT("MmFindGapAtAddress: %p [%p]\n", Address
,
669 (ULONG_PTR
)HighestAddress
- (ULONG_PTR
)Address
);
670 return (ULONG_PTR
)HighestAddress
- (ULONG_PTR
)Address
;
676 MiRemoveNode(IN PMMADDRESS_NODE Node
,
677 IN PMM_AVL_TABLE Table
);
683 MiRosCheckMemoryAreasRecursive(
686 /* Check if the allocation is ok */
687 ExpCheckPoolAllocation(Node
, NonPagedPool
, 'ERAM');
689 /* Check some fields */
690 ASSERT(Node
->Magic
== 'erAM');
691 ASSERT(PAGE_ALIGN(Node
->StartingAddress
) == Node
->StartingAddress
);
692 ASSERT(Node
->EndingAddress
!= NULL
);
693 ASSERT(PAGE_ALIGN(Node
->EndingAddress
) == Node
->EndingAddress
);
694 ASSERT((ULONG_PTR
)Node
->StartingAddress
< (ULONG_PTR
)Node
->EndingAddress
);
695 ASSERT((Node
->Type
== 0) ||
696 (Node
->Type
== MEMORY_AREA_CACHE
) ||
697 // (Node->Type == MEMORY_AREA_CACHE_SEGMENT) ||
698 (Node
->Type
== MEMORY_AREA_SECTION_VIEW
) ||
699 (Node
->Type
== MEMORY_AREA_OWNED_BY_ARM3
) ||
700 (Node
->Type
== (MEMORY_AREA_OWNED_BY_ARM3
| MEMORY_AREA_STATIC
)));
702 /* Recursively check children */
703 if (Node
->LeftChild
!= NULL
)
704 MiRosCheckMemoryAreasRecursive(Node
->LeftChild
);
705 if (Node
->RightChild
!= NULL
)
706 MiRosCheckMemoryAreasRecursive(Node
->RightChild
);
711 MiRosCheckMemoryAreas(
712 PMMSUPPORT AddressSpace
)
714 PMEMORY_AREA RootNode
;
715 PEPROCESS AddressSpaceOwner
;
716 BOOLEAN NeedReleaseLock
;
718 NeedReleaseLock
= FALSE
;
720 /* Get the address space owner */
721 AddressSpaceOwner
= CONTAINING_RECORD(AddressSpace
, EPROCESS
, Vm
);
723 /* Check if we already own the address space lock */
724 if (AddressSpaceOwner
->AddressCreationLock
.Owner
!= KeGetCurrentThread())
726 /* We must own it! */
727 MmLockAddressSpace(AddressSpace
);
728 NeedReleaseLock
= TRUE
;
731 /* Check all memory areas */
732 RootNode
= (PMEMORY_AREA
)AddressSpace
->WorkingSetExpansionLinks
.Flink
;
733 MiRosCheckMemoryAreasRecursive(RootNode
);
735 /* Release the lock, if we acquired it */
738 MmUnlockAddressSpace(AddressSpace
);
742 extern KGUARDED_MUTEX PspActiveProcessMutex
;
746 MiCheckAllProcessMemoryAreas(VOID
)
751 /* Acquire the Active Process Lock */
752 KeAcquireGuardedMutex(&PspActiveProcessMutex
);
754 /* Loop the process list */
755 Entry
= PsActiveProcessHead
.Flink
;
756 while (Entry
!= &PsActiveProcessHead
)
758 /* Get the process */
759 Process
= CONTAINING_RECORD(Entry
, EPROCESS
, ActiveProcessLinks
);
761 /* Check memory areas */
762 MiRosCheckMemoryAreas(&Process
->Vm
);
764 Entry
= Entry
->Flink
;
767 /* Release the lock */
768 KeReleaseGuardedMutex(&PspActiveProcessMutex
);
774 * @name MmFreeMemoryArea
776 * Free an existing memory area.
778 * @param AddressSpace
779 * Address space to free the area from.
781 * Memory area we're about to free.
783 * Callback function for each freed page.
784 * @param FreePageContext
785 * Context passed to the callback function.
789 * @remarks Lock the address space before calling this function.
793 MiDeletePte(IN PMMPTE PointerPte
,
794 IN PVOID VirtualAddress
,
795 IN PEPROCESS CurrentProcess
,
796 IN PMMPTE PrototypePte
);
800 PMMSUPPORT AddressSpace
,
801 PMEMORY_AREA MemoryArea
,
802 PMM_FREE_PAGE_FUNC FreePage
,
803 PVOID FreePageContext
)
805 PMEMORY_AREA
*ParentReplace
;
809 /* Make sure we own the address space lock! */
810 ASSERT(CONTAINING_RECORD(AddressSpace
, EPROCESS
, Vm
)->AddressCreationLock
.Owner
== KeGetCurrentThread());
813 ASSERT(MemoryArea
->Magic
== 'erAM');
815 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
817 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
818 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
820 if (Process
!= NULL
&&
821 Process
!= CurrentProcess
)
823 KeAttachProcess(&Process
->Pcb
);
826 EndAddress
= MM_ROUND_UP(MemoryArea
->EndingAddress
, PAGE_SIZE
);
827 for (Address
= (ULONG_PTR
)MemoryArea
->StartingAddress
;
828 Address
< (ULONG_PTR
)EndAddress
;
829 Address
+= PAGE_SIZE
)
831 BOOLEAN Dirty
= FALSE
;
832 SWAPENTRY SwapEntry
= 0;
835 if (MmIsPageSwapEntry(Process
, (PVOID
)Address
))
837 MmDeletePageFileMapping(Process
, (PVOID
)Address
, &SwapEntry
);
841 MmDeleteVirtualMapping(Process
, (PVOID
)Address
, FALSE
, &Dirty
, &Page
);
843 if (FreePage
!= NULL
)
845 FreePage(FreePageContext
, MemoryArea
, (PVOID
)Address
,
846 Page
, SwapEntry
, (BOOLEAN
)Dirty
);
848 #if (_MI_PAGING_LEVELS == 2)
849 /* Remove page table reference */
850 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
851 if ((SwapEntry
|| Page
) && ((PVOID
)Address
< MmSystemRangeStart
))
853 ASSERT(AddressSpace
!= MmGetKernelAddressSpace());
854 if (MiQueryPageTableReferences((PVOID
)Address
) == 0)
856 /* No PTE relies on this PDE. Release it */
857 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
858 PMMPDE PointerPde
= MiAddressToPde(Address
);
859 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
860 MiDeletePte(PointerPde
, MiPdeToPte(PointerPde
), Process
, NULL
);
861 ASSERT(PointerPde
->u
.Hard
.Valid
== 0);
862 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
868 if (Process
!= NULL
&&
869 Process
!= CurrentProcess
)
876 ASSERT(MemoryArea
->EndingAddress
< MmSystemRangeStart
);
877 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
|| MemoryArea
->Type
== MEMORY_AREA_CACHE
);
879 /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
880 ASSERT(((PMMVAD
)MemoryArea
->Vad
)->u
.VadFlags
.Spare
!= 0);
881 if (((PMMVAD
)MemoryArea
->Vad
)->u
.VadFlags
.Spare
== 1)
883 MiRemoveNode(MemoryArea
->Vad
, &Process
->VadRoot
);
886 ExFreePoolWithTag(MemoryArea
->Vad
, TAG_MVAD
);
887 MemoryArea
->Vad
= NULL
;
891 /* Remove the tree item. */
893 if (MemoryArea
->Parent
!= NULL
)
895 if (MemoryArea
->Parent
->LeftChild
== MemoryArea
)
896 ParentReplace
= &MemoryArea
->Parent
->LeftChild
;
898 ParentReplace
= &MemoryArea
->Parent
->RightChild
;
901 ParentReplace
= (PMEMORY_AREA
*)&AddressSpace
->WorkingSetExpansionLinks
.Flink
;
903 if (MemoryArea
->RightChild
== NULL
)
905 *ParentReplace
= MemoryArea
->LeftChild
;
906 if (MemoryArea
->LeftChild
)
907 MemoryArea
->LeftChild
->Parent
= MemoryArea
->Parent
;
911 if (MemoryArea
->RightChild
->LeftChild
== NULL
)
913 MemoryArea
->RightChild
->LeftChild
= MemoryArea
->LeftChild
;
914 if (MemoryArea
->LeftChild
)
915 MemoryArea
->LeftChild
->Parent
= MemoryArea
->RightChild
;
917 *ParentReplace
= MemoryArea
->RightChild
;
918 MemoryArea
->RightChild
->Parent
= MemoryArea
->Parent
;
922 PMEMORY_AREA LowestNode
;
924 LowestNode
= MemoryArea
->RightChild
->LeftChild
;
925 while (LowestNode
->LeftChild
!= NULL
)
926 LowestNode
= LowestNode
->LeftChild
;
928 LowestNode
->Parent
->LeftChild
= LowestNode
->RightChild
;
929 if (LowestNode
->RightChild
)
930 LowestNode
->RightChild
->Parent
= LowestNode
->Parent
;
932 LowestNode
->LeftChild
= MemoryArea
->LeftChild
;
933 if (MemoryArea
->LeftChild
)
934 MemoryArea
->LeftChild
->Parent
= LowestNode
;
936 LowestNode
->RightChild
= MemoryArea
->RightChild
;
937 MemoryArea
->RightChild
->Parent
= LowestNode
;
939 *ParentReplace
= LowestNode
;
940 LowestNode
->Parent
= MemoryArea
->Parent
;
945 ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
947 DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
949 return STATUS_SUCCESS
;
953 * @name MmCreateMemoryArea
955 * Create a memory area.
957 * @param AddressSpace
958 * Address space to create the area in.
960 * Type of the memory area.
962 * Base address for the memory area we're about the create. On
963 * input it contains either 0 (auto-assign address) or preferred
964 * address. On output it contains the starting address of the
965 * newly created area.
967 * Length of the area to allocate.
969 * Protection attributes for the memory area.
971 * Receives a pointer to the memory area on successful exit.
975 * @remarks Lock the address space before calling this function.
979 MmCreateMemoryArea(PMMSUPPORT AddressSpace
,
984 PMEMORY_AREA
*Result
,
985 BOOLEAN FixedAddress
,
986 ULONG AllocationFlags
,
990 PMEMORY_AREA MemoryArea
;
992 DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
993 "*BaseAddress %p, Length %p, AllocationFlags %x, "
994 "FixedAddress %x, Result %p)\n",
995 Type
, BaseAddress
, *BaseAddress
, Length
, AllocationFlags
,
996 FixedAddress
, Result
);
998 if ((*BaseAddress
) == 0 && !FixedAddress
)
1000 tmpLength
= (ULONG_PTR
)MM_ROUND_UP(Length
, Granularity
);
1001 *BaseAddress
= MmFindGap(AddressSpace
,
1004 (AllocationFlags
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
);
1005 if ((*BaseAddress
) == 0)
1007 DPRINT("No suitable gap\n");
1008 return STATUS_NO_MEMORY
;
1013 tmpLength
= Length
+ ((ULONG_PTR
) *BaseAddress
1014 - (ULONG_PTR
) MM_ROUND_DOWN(*BaseAddress
, Granularity
));
1015 tmpLength
= (ULONG_PTR
)MM_ROUND_UP(tmpLength
, Granularity
);
1016 *BaseAddress
= MM_ROUND_DOWN(*BaseAddress
, Granularity
);
1018 if (!MmGetAddressSpaceOwner(AddressSpace
) && *BaseAddress
< MmSystemRangeStart
)
1020 return STATUS_ACCESS_VIOLATION
;
1023 if (MmGetAddressSpaceOwner(AddressSpace
) &&
1024 (ULONG_PTR
)(*BaseAddress
) + tmpLength
> (ULONG_PTR
)MmSystemRangeStart
)
1026 DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
1027 return STATUS_ACCESS_VIOLATION
;
1030 if (MmLocateMemoryAreaByRegion(AddressSpace
,
1034 DPRINT("Memory area already occupied\n");
1035 return STATUS_CONFLICTING_ADDRESSES
;
1040 // Is this a static memory area?
1042 if (Type
& MEMORY_AREA_STATIC
)
1045 // Use the static array instead of the pool
1047 ASSERT(MiStaticMemoryAreaCount
< MI_STATIC_MEMORY_AREAS
);
1048 MemoryArea
= &MiStaticMemoryAreas
[MiStaticMemoryAreaCount
++];
1049 Type
&= ~MEMORY_AREA_STATIC
;
1054 // Allocate the memory area from nonpaged pool
1056 MemoryArea
= ExAllocatePoolWithTag(NonPagedPool
,
1057 sizeof(MEMORY_AREA
),
1063 DPRINT1("Not enough memory.\n");
1064 return STATUS_NO_MEMORY
;
1067 RtlZeroMemory(MemoryArea
, sizeof(MEMORY_AREA
));
1068 MemoryArea
->Type
= Type
;
1069 MemoryArea
->StartingAddress
= *BaseAddress
;
1070 MemoryArea
->EndingAddress
= (PVOID
)((ULONG_PTR
)*BaseAddress
+ tmpLength
);
1071 MemoryArea
->Protect
= Protect
;
1072 MemoryArea
->Flags
= AllocationFlags
;
1073 //MemoryArea->LockCount = 0;
1074 MemoryArea
->Magic
= 'erAM';
1075 MemoryArea
->DeleteInProgress
= FALSE
;
1077 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
1079 *Result
= MemoryArea
;
1081 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress
);
1082 return STATUS_SUCCESS
;
1086 MmMapMemoryArea(PVOID BaseAddress
,
1094 ASSERT(((ULONG_PTR
)BaseAddress
% PAGE_SIZE
) == 0);
1096 for (i
= 0; i
< PAGE_ROUND_UP(Length
) / PAGE_SIZE
; i
++)
1100 Status
= MmRequestPageMemoryConsumer(Consumer
, TRUE
, &Page
);
1101 if (!NT_SUCCESS(Status
))
1103 DPRINT1("Unable to allocate page\n");
1104 KeBugCheck(MEMORY_MANAGEMENT
);
1106 Status
= MmCreateVirtualMapping (NULL
,
1107 (PVOID
)((ULONG_PTR
)BaseAddress
+ (i
* PAGE_SIZE
)),
1111 if (!NT_SUCCESS(Status
))
1113 DPRINT1("Unable to create virtual mapping\n");
1114 KeBugCheck(MEMORY_MANAGEMENT
);
1121 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
);
1125 MmDeleteProcessAddressSpace(PEPROCESS Process
)
1128 PMEMORY_AREA MemoryArea
;
1130 DPRINT("MmDeleteProcessAddressSpace(Process %p (%s))\n", Process
,
1131 Process
->ImageFileName
);
1134 RemoveEntryList(&Process
->MmProcessLinks
);
1136 MmLockAddressSpace(&Process
->Vm
);
1138 while ((MemoryArea
= (PMEMORY_AREA
)Process
->Vm
.WorkingSetExpansionLinks
.Flink
) != NULL
)
1140 switch (MemoryArea
->Type
)
1142 case MEMORY_AREA_SECTION_VIEW
:
1143 Address
= (PVOID
)MemoryArea
->StartingAddress
;
1144 MmUnlockAddressSpace(&Process
->Vm
);
1145 MmUnmapViewOfSection(Process
, Address
);
1146 MmLockAddressSpace(&Process
->Vm
);
1149 case MEMORY_AREA_CACHE
:
1150 Address
= (PVOID
)MemoryArea
->StartingAddress
;
1151 MmUnlockAddressSpace(&Process
->Vm
);
1152 MmUnmapViewOfCacheSegment(&Process
->Vm
, Address
);
1153 MmLockAddressSpace(&Process
->Vm
);
1156 case MEMORY_AREA_OWNED_BY_ARM3
:
1157 MmFreeMemoryArea(&Process
->Vm
,
1164 KeBugCheck(MEMORY_MANAGEMENT
);
1168 #if (_MI_PAGING_LEVELS == 2)
1172 /* Attach to Process */
1173 KeAttachProcess(&Process
->Pcb
);
1175 /* Acquire PFN lock */
1176 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1178 for (Address
= MI_LOWEST_VAD_ADDRESS
;
1179 Address
< MM_HIGHEST_VAD_ADDRESS
;
1180 Address
=(PVOID
)((ULONG_PTR
)Address
+ (PAGE_SIZE
* PTE_COUNT
)))
1182 /* At this point all references should be dead */
1183 if (MiQueryPageTableReferences(Address
) != 0)
1185 DPRINT1("Process %p, Address %p, UsedPageTableEntries %lu\n",
1188 MiQueryPageTableReferences(Address
));
1189 ASSERT(MiQueryPageTableReferences(Address
) == 0);
1191 pointerPde
= MiAddressToPde(Address
);
1192 /* Unlike in ARM3, we don't necesarrily free the PDE page as soon as reference reaches 0,
1193 * so we must clean up a bit when process closes */
1194 if (pointerPde
->u
.Hard
.Valid
)
1195 MiDeletePte(pointerPde
, MiPdeToPte(pointerPde
), Process
, NULL
);
1196 ASSERT(pointerPde
->u
.Hard
.Valid
== 0);
1199 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1206 MmUnlockAddressSpace(&Process
->Vm
);
1208 DPRINT("Finished MmDeleteProcessAddressSpace()\n");
1209 MmDeleteProcessAddressSpace2(Process
);
1210 return(STATUS_SUCCESS
);