2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/vadnode.c
5 * PURPOSE: ARM Memory Manager VAD Node Algorithms
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 * Timo Kreuzer (timo.kreuzer@reactos.org)
10 /* INCLUDES *******************************************************************/
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
19 /* Include Mm version of AVL support */
21 #include <sdk/lib/rtl/avlsupp.c>
23 /* GLOBALS ********************************************************************/
25 CHAR MmReadWrite
[32] =
27 MM_NO_ACCESS_ALLOWED
, MM_READ_ONLY_ALLOWED
, MM_READ_ONLY_ALLOWED
,
28 MM_READ_ONLY_ALLOWED
, MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
29 MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
31 MM_NO_ACCESS_ALLOWED
, MM_READ_ONLY_ALLOWED
, MM_READ_ONLY_ALLOWED
,
32 MM_READ_ONLY_ALLOWED
, MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
33 MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
35 MM_NO_ACCESS_ALLOWED
, MM_READ_ONLY_ALLOWED
, MM_READ_ONLY_ALLOWED
,
36 MM_READ_ONLY_ALLOWED
, MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
37 MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
39 MM_NO_ACCESS_ALLOWED
, MM_READ_ONLY_ALLOWED
, MM_READ_ONLY_ALLOWED
,
40 MM_READ_ONLY_ALLOWED
, MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
41 MM_READ_WRITE_ALLOWED
, MM_READ_WRITE_ALLOWED
,
44 /* FUNCTIONS ******************************************************************/
46 extern MM_AVL_TABLE MiRosKernelVadRoot
;
52 MiDbgAssertIsLockedForRead(_In_ PMM_AVL_TABLE Table
)
54 if (Table
== &MmSectionBasedRoot
)
56 /* Need to hold MmSectionBasedMutex */
57 ASSERT(MmSectionBasedMutex
.Owner
== KeGetCurrentThread());
59 else if (Table
== &MiRosKernelVadRoot
)
61 /* Need to hold either the system working-set lock or
62 the idle process' AddressCreationLock */
63 ASSERT(PsGetCurrentThread()->OwnsSystemWorkingSetExclusive
||
64 PsGetCurrentThread()->OwnsSystemWorkingSetShared
||
65 (PsIdleProcess
->AddressCreationLock
.Owner
== KeGetCurrentThread()));
69 /* Need to hold either the process working-set lock or
70 the current process' AddressCreationLock */
71 PEPROCESS Process
= CONTAINING_RECORD(Table
, EPROCESS
, VadRoot
);
72 ASSERT(MI_WS_OWNER(Process
) ||
73 (Process
->AddressCreationLock
.Owner
== KeGetCurrentThread()));
79 MiDbgAssertIsLockedForWrite(_In_ PMM_AVL_TABLE Table
)
81 if (Table
== &MmSectionBasedRoot
)
83 /* Need to hold MmSectionBasedMutex */
84 ASSERT(MmSectionBasedMutex
.Owner
== KeGetCurrentThread());
86 else if (Table
== &MiRosKernelVadRoot
)
88 /* Need to hold both the system working-set lock exclusive and
89 the idle process' AddressCreationLock */
90 ASSERT(PsGetCurrentThread()->OwnsSystemWorkingSetExclusive
);
91 ASSERT(PsIdleProcess
->AddressCreationLock
.Owner
== KeGetCurrentThread());
95 /* Need to hold both the process working-set lock exclusive and
96 the current process' AddressCreationLock */
97 PEPROCESS Process
= CONTAINING_RECORD(Table
, EPROCESS
, VadRoot
);
98 ASSERT(Process
== PsGetCurrentProcess());
99 ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive
);
100 ASSERT(Process
->AddressCreationLock
.Owner
== KeGetCurrentThread());
104 #define ASSERT_LOCKED_FOR_READ(Table) MiDbgAssertIsLockedForRead(Table)
105 #define ASSERT_LOCKED_FOR_WRITE(Table) MiDbgAssertIsLockedForWrite(Table)
109 #define ASSERT_LOCKED_FOR_READ(Table)
110 #define ASSERT_LOCKED_FOR_WRITE(Table)
116 MiLocateAddress(IN PVOID VirtualAddress
)
120 PMM_AVL_TABLE Table
= &PsGetCurrentProcess()->VadRoot
;
121 TABLE_SEARCH_RESULT SearchResult
;
123 ASSERT_LOCKED_FOR_READ(Table
);
125 /* Start with the the hint */
126 FoundVad
= (PMMVAD
)Table
->NodeHint
;
127 if (!FoundVad
) return NULL
;
129 /* Check if this VPN is in the hint, if so, use it */
130 Vpn
= (ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
;
131 if ((Vpn
>= FoundVad
->StartingVpn
) && (Vpn
<= FoundVad
->EndingVpn
)) return FoundVad
;
133 /* VAD hint didn't work, go look for it */
134 SearchResult
= RtlpFindAvlTableNodeOrParent(Table
,
136 (PMMADDRESS_NODE
*)&FoundVad
);
137 if (SearchResult
!= TableFoundNode
) return NULL
;
139 /* We found it, update the hint */
140 ASSERT(FoundVad
!= NULL
);
141 ASSERT((Vpn
>= FoundVad
->StartingVpn
) && (Vpn
<= FoundVad
->EndingVpn
));
143 /* We allow this (atomic) update without exclusive lock, because it's a hint only */
144 Table
->NodeHint
= FoundVad
;
150 MiCheckForConflictingNode(IN ULONG_PTR StartVpn
,
152 IN PMM_AVL_TABLE Table
,
153 OUT PMMADDRESS_NODE
*NodeOrParent
)
155 PMMADDRESS_NODE ParentNode
, CurrentNode
;
157 ASSERT_LOCKED_FOR_READ(Table
);
159 /* If the tree is empty, there is no conflict */
160 if (Table
->NumberGenericTableElements
== 0) return TableEmptyTree
;
162 /* Start looping from the root node */
163 CurrentNode
= RtlRightChildAvl(&Table
->BalancedRoot
);
164 ASSERT(CurrentNode
!= NULL
);
167 ParentNode
= CurrentNode
;
169 /* This address comes after */
170 if (StartVpn
> CurrentNode
->EndingVpn
)
172 /* Keep searching on the right */
173 CurrentNode
= RtlRightChildAvl(CurrentNode
);
175 else if (EndVpn
< CurrentNode
->StartingVpn
)
177 /* This address ends before the node starts, search on the left */
178 CurrentNode
= RtlLeftChildAvl(CurrentNode
);
182 /* This address is part of this node, return it */
183 *NodeOrParent
= ParentNode
;
184 return TableFoundNode
;
188 /* There is no more child, save the current node as parent */
189 *NodeOrParent
= ParentNode
;
190 if (StartVpn
> ParentNode
->EndingVpn
)
192 return TableInsertAsRight
;
196 return TableInsertAsLeft
;
202 MiInsertNode(IN PMM_AVL_TABLE Table
,
203 IN PMMADDRESS_NODE NewNode
,
204 IN PMMADDRESS_NODE Parent
,
205 IN TABLE_SEARCH_RESULT Result
)
209 ASSERT_LOCKED_FOR_WRITE(Table
);
211 /* Insert it into the tree */
212 RtlpInsertAvlTreeNode(Table
, NewNode
, Parent
, Result
);
214 /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */
215 Vad
= (PMMVAD_LONG
)NewNode
;
216 if (Vad
->u
.VadFlags
.Spare
== 0)
219 PMEMORY_AREA MemoryArea
;
221 PEPROCESS Process
= CONTAINING_RECORD(Table
, EPROCESS
, VadRoot
);
222 PVOID AllocatedBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
224 Size
= ((Vad
->EndingVpn
+ 1) - Vad
->StartingVpn
) << PAGE_SHIFT
;
226 if (AllocatedBase
== NULL
)
228 AllocatedBase
= (PVOID
)(ULONG_PTR
)1;
232 Status
= MmCreateMemoryArea(&Process
->Vm
,
233 MEMORY_AREA_OWNED_BY_ARM3
,
240 ASSERT(NT_SUCCESS(Status
));
242 /* Check if this is VM VAD */
243 if (Vad
->ControlArea
== NULL
)
245 /* We store the reactos MEMORY_AREA here */
246 Vad
->FirstPrototypePte
= (PMMPTE
)MemoryArea
;
250 /* This is a section VAD. Store the MAREA here for now */
251 ASSERT(Vad
->u4
.Banked
== (PVOID
)(ULONG_PTR
)0xDEADBABEDEADBABEULL
);
252 Vad
->u4
.Banked
= (PVOID
)MemoryArea
;
259 MiInsertVad(IN PMMVAD Vad
,
260 IN PMM_AVL_TABLE VadRoot
)
262 TABLE_SEARCH_RESULT Result
;
263 PMMADDRESS_NODE Parent
= NULL
;
265 ASSERT_LOCKED_FOR_WRITE(VadRoot
);
267 /* Validate the VAD and set it as the current hint */
268 ASSERT(Vad
->EndingVpn
>= Vad
->StartingVpn
);
269 VadRoot
->NodeHint
= Vad
;
271 /* Find the parent VAD and where this child should be inserted */
272 Result
= RtlpFindAvlTableNodeOrParent(VadRoot
, (PVOID
)Vad
->StartingVpn
, &Parent
);
273 ASSERT(Result
!= TableFoundNode
);
274 ASSERT((Parent
!= NULL
) || (Result
== TableEmptyTree
));
276 /* Do the actual insert operation */
277 MiInsertNode(VadRoot
, (PVOID
)Vad
, Parent
, Result
);
284 _In_ ULONG_PTR
*BaseAddress
,
285 _In_ SIZE_T ViewSize
,
286 _In_ ULONG_PTR HighestAddress
,
287 _In_ ULONG_PTR Alignment
,
288 _In_ ULONG AllocationType
)
290 ULONG_PTR StartingAddress
, EndingAddress
;
291 PEPROCESS CurrentProcess
;
292 PETHREAD CurrentThread
;
293 TABLE_SEARCH_RESULT Result
;
294 PMMADDRESS_NODE Parent
;
296 /* Align the view size to pages */
297 ViewSize
= ALIGN_UP_BY(ViewSize
, PAGE_SIZE
);
299 /* Get the current process */
300 CurrentProcess
= PsGetCurrentProcess();
302 /* Acquire the address creation lock and make sure the process is alive */
303 KeAcquireGuardedMutex(&CurrentProcess
->AddressCreationLock
);
304 if (CurrentProcess
->VmDeleted
)
306 KeReleaseGuardedMutex(&CurrentProcess
->AddressCreationLock
);
307 DPRINT1("The process is dying\n");
308 return STATUS_PROCESS_IS_TERMINATING
;
311 /* Did the caller specify an address? */
312 if (*BaseAddress
== 0)
314 /* Make sure HighestAddress is not too large */
315 HighestAddress
= min(HighestAddress
, (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
);
317 /* Which way should we search? */
318 if ((AllocationType
& MEM_TOP_DOWN
) || CurrentProcess
->VmTopDown
)
320 /* Find an address top-down */
321 Result
= MiFindEmptyAddressRangeDownTree(ViewSize
,
324 &CurrentProcess
->VadRoot
,
330 /* Find an address bottom-up */
331 Result
= MiFindEmptyAddressRangeInTree(ViewSize
,
333 &CurrentProcess
->VadRoot
,
338 /* Get the ending address, which is the last piece we need for the VAD */
339 EndingAddress
= StartingAddress
+ ViewSize
- 1;
341 /* Check if we found a suitable location */
342 if ((Result
== TableFoundNode
) || (EndingAddress
> HighestAddress
))
344 DPRINT1("Not enough free space to insert this VAD node!\n");
345 KeReleaseGuardedMutex(&CurrentProcess
->AddressCreationLock
);
346 return STATUS_NO_MEMORY
;
349 ASSERT(StartingAddress
!= 0);
350 ASSERT(StartingAddress
< (ULONG_PTR
)HighestAddress
);
351 ASSERT(EndingAddress
> StartingAddress
);
355 /* Calculate the starting and ending address */
356 StartingAddress
= ALIGN_DOWN_BY(*BaseAddress
, Alignment
);
357 EndingAddress
= StartingAddress
+ ViewSize
- 1;
359 /* Make sure it doesn't conflict with an existing allocation */
360 Result
= MiCheckForConflictingNode(StartingAddress
>> PAGE_SHIFT
,
361 EndingAddress
>> PAGE_SHIFT
,
362 &CurrentProcess
->VadRoot
,
364 if (Result
== TableFoundNode
)
366 DPRINT("Given address conflicts with existing node\n");
367 KeReleaseGuardedMutex(&CurrentProcess
->AddressCreationLock
);
368 return STATUS_CONFLICTING_ADDRESSES
;
372 /* Now set the VAD address */
373 Vad
->StartingVpn
= StartingAddress
>> PAGE_SHIFT
;
374 Vad
->EndingVpn
= EndingAddress
>> PAGE_SHIFT
;
376 /* Check if we already need to charge for the pages */
377 if ((Vad
->u
.VadFlags
.PrivateMemory
&& Vad
->u
.VadFlags
.MemCommit
) ||
378 (!Vad
->u
.VadFlags
.PrivateMemory
&&
379 (Vad
->u
.VadFlags
.Protection
& PAGE_WRITECOPY
)))
381 /* Set the commit charge */
382 Vad
->u
.VadFlags
.CommitCharge
= ViewSize
/ PAGE_SIZE
;
385 /* Check if the VAD is to be secured */
386 if (Vad
->u2
.VadFlags2
.OneSecured
)
388 /* This *must* be a long VAD! */
389 ASSERT(Vad
->u2
.VadFlags2
.LongVad
);
391 /* Yeah this is retarded, I didn't invent it! */
392 ((PMMVAD_LONG
)Vad
)->u3
.Secured
.StartVpn
= StartingAddress
;
393 ((PMMVAD_LONG
)Vad
)->u3
.Secured
.EndVpn
= EndingAddress
;
396 /* Lock the working set */
397 CurrentThread
= PsGetCurrentThread();
398 MiLockProcessWorkingSetUnsafe(CurrentProcess
, CurrentThread
);
401 CurrentProcess
->VadRoot
.NodeHint
= Vad
;
402 MiInsertNode(&CurrentProcess
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
404 /* Release the working set */
405 MiUnlockProcessWorkingSetUnsafe(CurrentProcess
, CurrentThread
);
407 /* Update the process' virtual size, and peak virtual size */
408 CurrentProcess
->VirtualSize
+= ViewSize
;
409 if (CurrentProcess
->VirtualSize
> CurrentProcess
->PeakVirtualSize
)
411 CurrentProcess
->PeakVirtualSize
= CurrentProcess
->VirtualSize
;
414 /* Unlock the address space */
415 KeReleaseGuardedMutex(&CurrentProcess
->AddressCreationLock
);
417 *BaseAddress
= StartingAddress
;
418 return STATUS_SUCCESS
;
423 MiInsertBasedSection(IN PSECTION Section
)
425 TABLE_SEARCH_RESULT Result
;
426 PMMADDRESS_NODE Parent
= NULL
;
427 ASSERT(Section
->Address
.EndingVpn
>= Section
->Address
.StartingVpn
);
429 ASSERT_LOCKED_FOR_WRITE(&MmSectionBasedRoot
);
431 /* Find the parent VAD and where this child should be inserted */
432 Result
= RtlpFindAvlTableNodeOrParent(&MmSectionBasedRoot
, (PVOID
)Section
->Address
.StartingVpn
, &Parent
);
433 ASSERT(Result
!= TableFoundNode
);
434 ASSERT((Parent
!= NULL
) || (Result
== TableEmptyTree
));
435 MiInsertNode(&MmSectionBasedRoot
, &Section
->Address
, Parent
, Result
);
440 MiRemoveNode(IN PMMADDRESS_NODE Node
,
441 IN PMM_AVL_TABLE Table
)
445 ASSERT_LOCKED_FOR_WRITE(Table
);
447 /* Call the AVL code */
448 RtlpDeleteAvlTreeNode(Table
, Node
);
450 /* Decrease element count */
451 Table
->NumberGenericTableElements
--;
453 /* Check if this node was the hint */
454 if (Table
->NodeHint
== Node
)
456 /* Get a new hint, unless we're empty now, in which case nothing */
457 if (!Table
->NumberGenericTableElements
) Table
->NodeHint
= NULL
;
458 else Table
->NodeHint
= Table
->BalancedRoot
.RightChild
;
461 /* Free the node from ReactOS view as well */
462 Vad
= (PMMVAD_LONG
)Node
;
463 if ((Table
!= &MmSectionBasedRoot
) && (Vad
->u
.VadFlags
.Spare
== 0))
465 PMEMORY_AREA MemoryArea
;
468 /* Check if this is VM VAD */
469 if (Vad
->ControlArea
== NULL
)
471 /* We store the ReactOS MEMORY_AREA here */
472 MemoryArea
= (PMEMORY_AREA
)Vad
->FirstPrototypePte
;
476 /* This is a section VAD. We store the ReactOS MEMORY_AREA here */
477 MemoryArea
= (PMEMORY_AREA
)Vad
->u4
.Banked
;
480 /* Make sure one actually still exists */
483 /* Make sure we have not already freed it */
484 ASSERT(MemoryArea
!= (PVOID
)(ULONG_PTR
)0xDEADBAB1DEADBAB1ULL
);
486 /* Get the process */
487 Process
= CONTAINING_RECORD(Table
, EPROCESS
, VadRoot
);
489 /* We only create fake memory-areas for ARM3 VADs */
490 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
491 ASSERT(MemoryArea
->Vad
== NULL
);
494 MmFreeMemoryArea(&Process
->Vm
, MemoryArea
, NULL
, NULL
);
496 /* Check if this is VM VAD */
497 if (Vad
->ControlArea
== NULL
)
499 /* Delete the pointer to it */
500 Vad
->FirstPrototypePte
= (PVOID
)(ULONG_PTR
)0xDEADBAB1DEADBAB1ULL
;
504 /* Delete the pointer to it */
505 Vad
->u4
.Banked
= (PVOID
)(ULONG_PTR
)0xDEADBAB1DEADBAB1ULL
;
513 MiGetPreviousNode(IN PMMADDRESS_NODE Node
)
515 PMMADDRESS_NODE Parent
;
517 /* Get the left child */
518 if (RtlLeftChildAvl(Node
))
520 /* Get right-most child */
521 Node
= RtlLeftChildAvl(Node
);
522 while (RtlRightChildAvl(Node
)) Node
= RtlRightChildAvl(Node
);
526 Parent
= RtlParentAvl(Node
);
527 ASSERT(Parent
!= NULL
);
528 while (Parent
!= Node
)
530 /* The parent should be a right child, return the real predecessor */
531 if (RtlIsRightChildAvl(Node
))
533 /* Return it unless it's the root */
534 if (Parent
== RtlParentAvl(Parent
)) Parent
= NULL
;
538 /* Keep lopping until we find our parent */
540 Parent
= RtlParentAvl(Node
);
549 MiGetNextNode(IN PMMADDRESS_NODE Node
)
551 PMMADDRESS_NODE Parent
;
553 /* Get the right child */
554 if (RtlRightChildAvl(Node
))
556 /* Get left-most child */
557 Node
= RtlRightChildAvl(Node
);
558 while (RtlLeftChildAvl(Node
)) Node
= RtlLeftChildAvl(Node
);
562 Parent
= RtlParentAvl(Node
);
563 ASSERT(Parent
!= NULL
);
564 while (Parent
!= Node
)
566 /* The parent should be a left child, return the real predecessor */
567 if (RtlIsLeftChildAvl(Node
))
573 /* Keep lopping until we find our parent */
575 Parent
= RtlParentAvl(Node
);
584 MiFindEmptyAddressRangeInTree(IN SIZE_T Length
,
585 IN ULONG_PTR Alignment
,
586 IN PMM_AVL_TABLE Table
,
587 OUT PMMADDRESS_NODE
*PreviousVad
,
590 PMMADDRESS_NODE Node
, PreviousNode
;
591 ULONG_PTR PageCount
, AlignmentVpn
, LowVpn
, HighestVpn
;
594 ASSERT_LOCKED_FOR_READ(Table
);
596 /* Calculate page numbers for the length, alignment, and starting address */
597 PageCount
= BYTES_TO_PAGES(Length
);
598 AlignmentVpn
= Alignment
>> PAGE_SHIFT
;
599 LowVpn
= ALIGN_UP_BY((ULONG_PTR
)MM_LOWEST_USER_ADDRESS
>> PAGE_SHIFT
, AlignmentVpn
);
601 /* Check for kernel mode table (memory areas) */
602 if (Table
->Unused
== 1)
604 LowVpn
= ALIGN_UP_BY((ULONG_PTR
)MmSystemRangeStart
>> PAGE_SHIFT
, AlignmentVpn
);
607 /* Check if the table is empty */
608 if (Table
->NumberGenericTableElements
== 0)
610 /* Tree is empty, the candidate address is already the best one */
611 *Base
= LowVpn
<< PAGE_SHIFT
;
612 return TableEmptyTree
;
615 /* Otherwise, follow the leftmost child of the right root node's child */
616 Node
= RtlRightChildAvl(&Table
->BalancedRoot
);
617 while (RtlLeftChildAvl(Node
)) Node
= RtlLeftChildAvl(Node
);
619 /* Start a search to find a gap */
623 /* Check if the gap below the current node is suitable */
624 if (Node
->StartingVpn
>= LowVpn
+ PageCount
)
626 /* There is enough space to add our node */
627 *Base
= LowVpn
<< PAGE_SHIFT
;
629 /* Can we use the current node as parent? */
630 if (RtlLeftChildAvl(Node
) == NULL
)
632 /* Node has no left child, so use it as parent */
634 return TableInsertAsLeft
;
638 /* Node has a left child, this means that the previous node is
639 the right-most child of it's left child and can be used as
640 the parent. In case we use the space before the left-most
641 node, it's left child must be NULL. */
642 ASSERT(PreviousNode
!= NULL
);
643 ASSERT(RtlRightChildAvl(PreviousNode
) == NULL
);
644 *PreviousVad
= PreviousNode
;
645 return TableInsertAsRight
;
649 /* The next candidate is above the current node */
650 if (Node
->EndingVpn
>= LowVpn
)
651 LowVpn
= ALIGN_UP_BY(Node
->EndingVpn
+ 1, AlignmentVpn
);
653 /* Remember the current node and go to the next node */
655 Node
= MiGetNextNode(Node
);
658 /* We're up to the highest VAD, will this allocation fit above it? */
659 HighestVpn
= ((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) / PAGE_SIZE
;
661 /* Check for kernel mode table (memory areas) */
662 if (Table
->Unused
== 1)
664 HighestVpn
= ALIGN_UP_BY((ULONG_PTR
)(LONG_PTR
)-1 >> PAGE_SHIFT
, AlignmentVpn
);
667 if (HighestVpn
>= LowVpn
+ PageCount
)
669 /* Yes! Use this VAD to store the allocation */
670 *PreviousVad
= PreviousNode
;
671 *Base
= LowVpn
<< PAGE_SHIFT
;
672 return TableInsertAsRight
;
675 /* Nyet, there's no free address space for this allocation, so we'll fail */
676 return TableFoundNode
;
681 MiFindEmptyAddressRangeDownTree(IN SIZE_T Length
,
682 IN ULONG_PTR BoundaryAddress
,
683 IN ULONG_PTR Alignment
,
684 IN PMM_AVL_TABLE Table
,
686 OUT PMMADDRESS_NODE
*Parent
)
688 PMMADDRESS_NODE Node
, OldNode
= NULL
, Child
;
689 ULONG_PTR LowVpn
, HighVpn
, AlignmentVpn
;
690 PFN_NUMBER PageCount
;
692 ASSERT_LOCKED_FOR_READ(Table
);
695 ASSERT(BoundaryAddress
);
696 ASSERT(BoundaryAddress
<= ((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
));
697 ASSERT((Alignment
& (PAGE_SIZE
- 1)) == 0);
699 /* Calculate page numbers for the length and alignment */
700 Length
= ROUND_TO_PAGES(Length
);
701 PageCount
= Length
>> PAGE_SHIFT
;
702 AlignmentVpn
= Alignment
/ PAGE_SIZE
;
704 /* Check for kernel mode table (memory areas) */
705 if (Table
->Unused
== 1)
707 LowVpn
= ALIGN_UP_BY((ULONG_PTR
)MmSystemRangeStart
>> PAGE_SHIFT
, AlignmentVpn
);
711 LowVpn
= ALIGN_UP_BY((ULONG_PTR
)MM_LOWEST_USER_ADDRESS
, Alignment
);
714 /* Check if there is enough space below the boundary */
715 if ((LowVpn
+ Length
) > (BoundaryAddress
+ 1))
717 return TableFoundNode
;
720 /* Check if the table is empty */
721 if (Table
->NumberGenericTableElements
== 0)
723 /* Tree is empty, the candidate address is already the best one */
724 *Base
= ALIGN_DOWN_BY(BoundaryAddress
+ 1 - Length
, Alignment
);
725 return TableEmptyTree
;
728 /* Calculate the initial upper margin */
729 HighVpn
= (BoundaryAddress
+ 1) >> PAGE_SHIFT
;
731 /* Starting from the root, follow the right children until we found a node
732 that ends above the boundary */
733 Node
= RtlRightChildAvl(&Table
->BalancedRoot
);
734 while ((Node
->EndingVpn
< HighVpn
) &&
735 ((Child
= RtlRightChildAvl(Node
)) != NULL
)) Node
= Child
;
737 /* Now loop the Vad nodes */
740 /* Calculate the lower margin */
741 LowVpn
= ALIGN_UP_BY(Node
->EndingVpn
+ 1, AlignmentVpn
);
743 /* Check if the current bounds are suitable */
744 if ((HighVpn
> LowVpn
) && ((HighVpn
- LowVpn
) >= PageCount
))
746 /* There is enough space to add our node */
747 LowVpn
= ALIGN_DOWN_BY(HighVpn
- PageCount
, AlignmentVpn
);
748 *Base
= LowVpn
<< PAGE_SHIFT
;
750 /* Can we use the current node as parent? */
751 if (!RtlRightChildAvl(Node
))
753 /* Node has no right child, so use it as parent */
755 return TableInsertAsRight
;
759 /* Node has a right child. This means we must have already
760 moved one node left from the right-most node we started
761 with, thus we already have an OldNode! */
762 ASSERT(OldNode
!= NULL
);
764 /* The node we had before is the most left grandchild of
765 that right child, use it as parent. */
766 ASSERT(RtlLeftChildAvl(OldNode
) == NULL
);
768 return TableInsertAsLeft
;
772 /* Update the upper margin if necessary */
773 if (Node
->StartingVpn
< HighVpn
) HighVpn
= Node
->StartingVpn
;
775 /* Remember the current node and go to the previous node */
777 Node
= MiGetPreviousNode(Node
);
780 /* Check if there's enough space before the lowest Vad */
781 LowVpn
= ALIGN_UP_BY((ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
, Alignment
) / PAGE_SIZE
;
782 if ((HighVpn
> LowVpn
) && ((HighVpn
- LowVpn
) >= PageCount
))
784 /* There is enough space to add our address */
785 LowVpn
= ALIGN_DOWN_BY(HighVpn
- PageCount
, Alignment
>> PAGE_SHIFT
);
786 *Base
= LowVpn
<< PAGE_SHIFT
;
788 return TableInsertAsLeft
;
791 /* No address space left at all */
794 return TableFoundNode
;
799 MiFindEmptyAddressRangeDownBasedTree(IN SIZE_T Length
,
800 IN ULONG_PTR BoundaryAddress
,
801 IN ULONG_PTR Alignment
,
802 IN PMM_AVL_TABLE Table
,
805 PMMADDRESS_NODE Node
, LowestNode
;
806 ULONG_PTR LowVpn
, BestVpn
;
808 ASSERT_LOCKED_FOR_READ(Table
);
811 ASSERT(Table
== &MmSectionBasedRoot
);
812 ASSERT(BoundaryAddress
);
813 ASSERT(BoundaryAddress
<= ((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1));
815 /* Compute page length, make sure the boundary address is valid */
816 Length
= ROUND_TO_PAGES(Length
);
817 if ((BoundaryAddress
+ 1) < Length
) return STATUS_NO_MEMORY
;
819 /* Check if the table is empty */
820 BestVpn
= ROUND_DOWN(BoundaryAddress
+ 1 - Length
, Alignment
);
821 if (Table
->NumberGenericTableElements
== 0)
823 /* Tree is empty, the candidate address is already the best one */
825 return STATUS_SUCCESS
;
828 /* Go to the right-most node which should be the biggest address */
829 Node
= Table
->BalancedRoot
.RightChild
;
830 while (RtlRightChildAvl(Node
)) Node
= RtlRightChildAvl(Node
);
832 /* Check if we can fit in here */
833 LowVpn
= ROUND_UP(Node
->EndingVpn
+ 1, Alignment
);
834 if ((LowVpn
< BoundaryAddress
) && (Length
<= (BoundaryAddress
- LowVpn
)))
836 #if (NTDDI_VERSION >= NTDDI_VISTA)
837 /* Return the address. */
840 /* Note: this is a compatibility hack that mimics a bug in the 2k3
841 kernel. It will can waste up to Alignment bytes of memory above
842 the allocation. This bug was fixed in Windows Vista */
843 *Base
= ROUND_DOWN(BoundaryAddress
- Length
, Alignment
);
845 return STATUS_SUCCESS
;
848 /* Now loop the Vad nodes */
851 /* Break out if we've reached the last node */
852 LowestNode
= MiGetPreviousNode(Node
);
853 if (!LowestNode
) break;
855 /* Check if this node could contain the requested address */
856 LowVpn
= ROUND_UP(LowestNode
->EndingVpn
+ 1, Alignment
);
857 if ((LowestNode
->EndingVpn
< BestVpn
) &&
858 (LowVpn
< Node
->StartingVpn
) &&
859 (Length
<= (Node
->StartingVpn
- LowVpn
)))
861 /* Check if we need to take BoundaryAddress into account */
862 if (BoundaryAddress
< Node
->StartingVpn
)
864 /* Return the optimal VPN address */
866 return STATUS_SUCCESS
;
870 /* The upper margin is given by the Node's starting address */
871 *Base
= ROUND_DOWN(Node
->StartingVpn
- Length
, Alignment
);
872 return STATUS_SUCCESS
;
876 /* Move to the next node */
880 /* Check if there's enough space before the lowest Vad */
881 if ((Node
->StartingVpn
> (ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
) &&
882 ((Node
->StartingVpn
- (ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
) >= Length
))
884 /* Check if it fits in perfectly */
885 if (BoundaryAddress
< Node
->StartingVpn
)
887 /* Return the optimal VPN address */
889 return STATUS_SUCCESS
;
892 /* Return an aligned base address within this node */
893 *Base
= ROUND_DOWN(Node
->StartingVpn
- Length
, Alignment
);
894 return STATUS_SUCCESS
;
897 /* No address space left at all */
898 return STATUS_NO_MEMORY
;
903 MiCheckSecuredVad(IN PMMVAD Vad
,
906 IN ULONG ProtectionMask
)
908 ULONG_PTR StartAddress
, EndAddress
;
910 /* Compute start and end address */
911 StartAddress
= (ULONG_PTR
)Base
;
912 EndAddress
= StartAddress
+ Size
- 1;
914 /* Are we deleting/unmapping, or changing? */
915 if (ProtectionMask
< MM_DELETE_CHECK
)
917 /* Changing... are we allowed to do so? */
918 if ((Vad
->u
.VadFlags
.NoChange
== 1) &&
919 (Vad
->u2
.VadFlags2
.SecNoChange
== 1) &&
920 (Vad
->u
.VadFlags
.Protection
!= ProtectionMask
))
923 DPRINT1("Trying to mess with a no-change VAD!\n");
924 return STATUS_INVALID_PAGE_PROTECTION
;
929 /* This is allowed */
933 /* ARM3 doesn't support this yet */
934 ASSERT(Vad
->u2
.VadFlags2
.MultipleSecured
== 0);
936 /* Is this a one-secured VAD, like a TEB or PEB? */
937 if (Vad
->u2
.VadFlags2
.OneSecured
)
939 /* Is this allocation being described by the VAD? */
940 if ((StartAddress
<= ((PMMVAD_LONG
)Vad
)->u3
.Secured
.EndVpn
) &&
941 (EndAddress
>= ((PMMVAD_LONG
)Vad
)->u3
.Secured
.StartVpn
))
944 if (ProtectionMask
& MM_DECOMMIT
)
946 DPRINT1("Not allowed to change protection on guard page!\n");
947 return STATUS_INVALID_PAGE_PROTECTION
;
950 /* ARM3 doesn't have read-only VADs yet */
951 ASSERT(Vad
->u2
.VadFlags2
.ReadOnly
== 0);
953 /* Check if read-write protections are allowed */
954 if (MmReadWrite
[ProtectionMask
] < MM_READ_WRITE_ALLOWED
)
956 DPRINT1("Invalid protection mask for RW access!\n");
957 return STATUS_INVALID_PAGE_PROTECTION
;
962 /* All good, allow the change */
963 return STATUS_SUCCESS
;