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 "../ARM3/miarm.h"
19 /* Include Mm version of AVL support */
20 #include "../ARM3/miavl.h"
21 #include "../../../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 ******************************************************************/
48 MiLocateAddress(IN PVOID VirtualAddress
)
52 PMM_AVL_TABLE Table
= &PsGetCurrentProcess()->VadRoot
;
53 TABLE_SEARCH_RESULT SearchResult
;
55 /* Start with the the hint */
56 FoundVad
= (PMMVAD
)Table
->NodeHint
;
57 if (!FoundVad
) return NULL
;
59 /* Check if this VPN is in the hint, if so, use it */
60 Vpn
= (ULONG_PTR
)VirtualAddress
>> PAGE_SHIFT
;
61 if ((Vpn
>= FoundVad
->StartingVpn
) && (Vpn
<= FoundVad
->EndingVpn
)) return FoundVad
;
63 /* VAD hint didn't work, go look for it */
64 SearchResult
= RtlpFindAvlTableNodeOrParent(Table
,
66 (PMMADDRESS_NODE
*)&FoundVad
);
67 if (SearchResult
!= TableFoundNode
) return NULL
;
69 /* We found it, update the hint */
70 ASSERT(FoundVad
!= NULL
);
71 ASSERT((Vpn
>= FoundVad
->StartingVpn
) && (Vpn
<= FoundVad
->EndingVpn
));
72 Table
->NodeHint
= FoundVad
;
78 MiCheckForConflictingNode(IN ULONG_PTR StartVpn
,
80 IN PMM_AVL_TABLE Table
)
82 PMMADDRESS_NODE CurrentNode
;
84 /* If the tree is empty, there is no conflict */
85 if (!Table
->NumberGenericTableElements
) return NULL
;
87 /* Start looping from the right */
88 CurrentNode
= RtlRightChildAvl(&Table
->BalancedRoot
);
89 ASSERT(CurrentNode
!= NULL
);
92 /* This address comes after */
93 if (StartVpn
> CurrentNode
->EndingVpn
)
95 /* Keep searching on the right */
96 CurrentNode
= RtlRightChildAvl(CurrentNode
);
98 else if (EndVpn
< CurrentNode
->StartingVpn
)
100 /* This address ends before the node starts, search on the left */
101 CurrentNode
= RtlLeftChildAvl(CurrentNode
);
105 /* This address is part of this node, return it */
110 /* Return either the conflicting node, or no node at all */
116 MiInsertNode(IN PMM_AVL_TABLE Table
,
117 IN PMMADDRESS_NODE NewNode
,
118 IN PMMADDRESS_NODE Parent
,
119 IN TABLE_SEARCH_RESULT Result
)
123 /* Insert it into the tree */
124 RtlpInsertAvlTreeNode(Table
, NewNode
, Parent
, Result
);
126 /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */
127 Vad
= (PMMVAD_LONG
)NewNode
;
128 if (Vad
->u
.VadFlags
.Spare
== 0)
131 PMEMORY_AREA MemoryArea
;
132 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
134 PEPROCESS Process
= CONTAINING_RECORD(Table
, EPROCESS
, VadRoot
);
135 PVOID AllocatedBase
= (PVOID
)(Vad
->StartingVpn
<< PAGE_SHIFT
);
136 BoundaryAddressMultiple
.QuadPart
= 0;
137 Size
= ((Vad
->EndingVpn
+ 1) - Vad
->StartingVpn
) << PAGE_SHIFT
;
138 Status
= MmCreateMemoryArea(&Process
->Vm
,
139 MEMORY_AREA_OWNED_BY_ARM3
,
146 BoundaryAddressMultiple
);
147 ASSERT(NT_SUCCESS(Status
));
149 /* Check if this is VM VAD */
150 if (Vad
->ControlArea
== NULL
)
152 /* We store the reactos MEMORY_AREA here */
153 Vad
->FirstPrototypePte
= (PMMPTE
)MemoryArea
;
157 /* This is a section VAD. Store the MAREA here for now */
158 ASSERT(Vad
->u4
.Banked
== (PVOID
)0xDEADBABE);
159 Vad
->u4
.Banked
= (PVOID
)MemoryArea
;
166 MiInsertVad(IN PMMVAD Vad
,
167 IN PEPROCESS Process
)
169 TABLE_SEARCH_RESULT Result
;
170 PMMADDRESS_NODE Parent
= NULL
;
172 /* Validate the VAD and set it as the current hint */
173 ASSERT(Vad
->EndingVpn
>= Vad
->StartingVpn
);
174 Process
->VadRoot
.NodeHint
= Vad
;
176 /* Find the parent VAD and where this child should be inserted */
177 Result
= RtlpFindAvlTableNodeOrParent(&Process
->VadRoot
, (PVOID
)Vad
->StartingVpn
, &Parent
);
178 ASSERT(Result
!= TableFoundNode
);
179 ASSERT((Parent
!= NULL
) || (Result
== TableEmptyTree
));
181 /* Do the actual insert operation */
182 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
187 MiInsertBasedSection(IN PSECTION Section
)
189 TABLE_SEARCH_RESULT Result
;
190 PMMADDRESS_NODE Parent
= NULL
;
191 ASSERT(Section
->Address
.EndingVpn
>= Section
->Address
.StartingVpn
);
193 /* Find the parent VAD and where this child should be inserted */
194 Result
= RtlpFindAvlTableNodeOrParent(&MmSectionBasedRoot
, (PVOID
)Section
->Address
.StartingVpn
, &Parent
);
195 ASSERT(Result
!= TableFoundNode
);
196 ASSERT((Parent
!= NULL
) || (Result
== TableEmptyTree
));
197 MiInsertNode(&MmSectionBasedRoot
, &Section
->Address
, Parent
, Result
);
202 MiRemoveNode(IN PMMADDRESS_NODE Node
,
203 IN PMM_AVL_TABLE Table
)
207 /* Call the AVL code */
208 RtlpDeleteAvlTreeNode(Table
, Node
);
210 /* Decrease element count */
211 Table
->NumberGenericTableElements
--;
213 /* Check if this node was the hint */
214 if (Table
->NodeHint
== Node
)
216 /* Get a new hint, unless we're empty now, in which case nothing */
217 if (!Table
->NumberGenericTableElements
) Table
->NodeHint
= NULL
;
218 else Table
->NodeHint
= Table
->BalancedRoot
.RightChild
;
221 /* Free the node from ReactOS view as well */
222 Vad
= (PMMVAD_LONG
)Node
;
223 if (Vad
->u
.VadFlags
.Spare
== 0)
225 PMEMORY_AREA MemoryArea
;
228 /* Check if this is VM VAD */
229 if (Vad
->ControlArea
== NULL
)
231 /* We store the ReactOS MEMORY_AREA here */
232 MemoryArea
= (PMEMORY_AREA
)Vad
->FirstPrototypePte
;
236 /* This is a section VAD. We store the ReactOS MEMORY_AREA here */
237 MemoryArea
= (PMEMORY_AREA
)Vad
->u4
.Banked
;
240 /* Make sure one actually still exists */
243 /* Make sure we have not already freed it */
244 ASSERT(MemoryArea
!= (PVOID
)0xDEADBAB1);
246 /* Get the process */
247 Process
= CONTAINING_RECORD(Table
, EPROCESS
, VadRoot
);
249 /* We only create fake memory-areas for ARM3 VADs */
250 ASSERT(MemoryArea
->Type
== MEMORY_AREA_OWNED_BY_ARM3
);
251 ASSERT(MemoryArea
->Vad
== NULL
);
254 MmFreeMemoryArea(&Process
->Vm
, MemoryArea
, NULL
, NULL
);
256 /* Check if this is VM VAD */
257 if (Vad
->ControlArea
== NULL
)
259 /* Delete the pointer to it */
260 Vad
->FirstPrototypePte
= (PVOID
)0xDEADBAB1;
264 /* Delete the pointer to it */
265 Vad
->u4
.Banked
= (PVOID
)0xDEADBAB1;
273 MiGetPreviousNode(IN PMMADDRESS_NODE Node
)
275 PMMADDRESS_NODE Parent
;
277 /* Get the left child */
278 if (RtlLeftChildAvl(Node
))
280 /* Get right-most child */
281 Node
= RtlLeftChildAvl(Node
);
282 while (RtlRightChildAvl(Node
)) Node
= RtlRightChildAvl(Node
);
286 Parent
= RtlParentAvl(Node
);
287 ASSERT(Parent
!= NULL
);
288 while (Parent
!= Node
)
290 /* The parent should be a right child, return the real predecessor */
291 if (RtlIsRightChildAvl(Node
))
293 /* Return it unless it's the root */
294 if (Parent
== RtlParentAvl(Parent
)) Parent
= NULL
;
298 /* Keep lopping until we find our parent */
300 Parent
= RtlParentAvl(Node
);
309 MiGetNextNode(IN PMMADDRESS_NODE Node
)
311 PMMADDRESS_NODE Parent
;
313 /* Get the right child */
314 if (RtlRightChildAvl(Node
))
316 /* Get left-most child */
317 Node
= RtlRightChildAvl(Node
);
318 while (RtlLeftChildAvl(Node
)) Node
= RtlLeftChildAvl(Node
);
322 Parent
= RtlParentAvl(Node
);
323 ASSERT(Parent
!= NULL
);
324 while (Parent
!= Node
)
326 /* The parent should be a left child, return the real predecessor */
327 if (RtlIsLeftChildAvl(Node
))
333 /* Keep lopping until we find our parent */
335 Parent
= RtlParentAvl(Node
);
344 MiFindEmptyAddressRangeInTree(IN SIZE_T Length
,
345 IN ULONG_PTR Alignment
,
346 IN PMM_AVL_TABLE Table
,
347 OUT PMMADDRESS_NODE
*PreviousVad
,
350 PMMADDRESS_NODE Node
;
351 PMMADDRESS_NODE NextNode
;
352 ULONG_PTR StartingVpn
, HighestVpn
, AlignmentVpn
, LengthVpn
, LowVpn
;
355 /* Precompute page numbers for the length, alignment, and starting address */
356 LengthVpn
= (Length
+ (PAGE_SIZE
- 1)) >> PAGE_SHIFT
;
357 AlignmentVpn
= Alignment
>> PAGE_SHIFT
;
358 StartingVpn
= ROUND_UP((ULONG_PTR
)MM_LOWEST_USER_ADDRESS
>> PAGE_SHIFT
,
361 /* Check if the table is free, so the lowest possible address is available */
362 if (!Table
->NumberGenericTableElements
) goto FoundAtBottom
;
364 /* Otherwise, follow the leftmost child of the right root node's child */
365 Node
= RtlRightChildAvl(&Table
->BalancedRoot
);
366 while (RtlLeftChildAvl(Node
)) Node
= RtlLeftChildAvl(Node
);
368 /* This is the node for the remaining gap at the bottom, can it be used? */
369 if ((Node
->StartingVpn
> StartingVpn
) &&
370 (LengthVpn
< Node
->StartingVpn
- StartingVpn
))
373 /* Use this VAD to store the allocation */
375 *Base
= StartingVpn
<< PAGE_SHIFT
;
376 return STATUS_SUCCESS
;
379 /* Otherwise, we start a search to find a gap */
382 /* The last aligned page number in this entry */
383 LowVpn
= ROUND_UP(Node
->EndingVpn
+ 1, AlignmentVpn
);
385 /* Keep going as long as there's still a next node */
386 NextNode
= MiGetNextNode(Node
);
387 if (!NextNode
) break;
389 /* Can this allocation fit in this node? */
390 if ((LengthVpn
<= (NextNode
->StartingVpn
- LowVpn
)) &&
391 (NextNode
->StartingVpn
> LowVpn
))
394 /* Yes! Use this VAD to store the allocation */
396 *Base
= ROUND_UP((Node
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1),
398 return STATUS_SUCCESS
;
401 /* Try the next node */
405 /* We're down to the last (top) VAD, will this allocation fit inside it? */
406 HighestVpn
= ((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) >> PAGE_SHIFT
;
407 if ((HighestVpn
> LowVpn
) && (LengthVpn
<= HighestVpn
- LowVpn
)) goto Found
;
409 /* Nyet, there's no free address space for this allocation, so we'll fail */
410 return STATUS_NO_MEMORY
;
415 MiFindEmptyAddressRangeDownTree(IN SIZE_T Length
,
416 IN ULONG_PTR BoundaryAddress
,
417 IN ULONG_PTR Alignment
,
418 IN PMM_AVL_TABLE Table
,
420 OUT PMMADDRESS_NODE
*Parent
)
422 PMMADDRESS_NODE Node
, LowestNode
, Child
;
423 ULONG_PTR LowVpn
, HighVpn
;
424 PFN_NUMBER PageCount
;
427 ASSERT(BoundaryAddress
);
428 ASSERT(BoundaryAddress
<= ((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1));
430 /* Compute page length, make sure the boundary address is valid */
431 Length
= ROUND_TO_PAGES(Length
);
432 PageCount
= Length
>> PAGE_SHIFT
;
433 if ((BoundaryAddress
+ 1) < Length
) return TableFoundNode
;
435 /* Check if the table is empty */
436 if (Table
->NumberGenericTableElements
== 0)
438 /* Tree is empty, the candidate address is already the best one */
439 *Base
= ROUND_UP(BoundaryAddress
+ 1 - Length
, Alignment
);
440 return TableEmptyTree
;
443 /* Calculate the initial upper margin */
444 HighVpn
= BoundaryAddress
>> PAGE_SHIFT
;
446 /* Starting from the root, go down until the right-most child
447 * which is just behind the boundary*/
448 LowestNode
= Node
= RtlRightChildAvl(&Table
->BalancedRoot
);
449 while (((Child
= RtlRightChildAvl(Node
)) != 0 )
450 && (Node
->EndingVpn
< HighVpn
)) Node
= Child
;
452 /* Now loop the Vad nodes */
455 /* Keep track of the lowest node */
458 /* Calculate the lower margin */
459 LowVpn
= ROUND_UP(Node
->EndingVpn
+ 1, Alignment
>> PAGE_SHIFT
);
461 /* Check if the current bounds are suitable */
462 if ((HighVpn
> LowVpn
) && ((HighVpn
- LowVpn
) >= PageCount
))
464 /* There is enough space to add our node */
465 LowVpn
= HighVpn
- PageCount
;
466 *Base
= LowVpn
<< PAGE_SHIFT
;
468 /* Can we use the current node as parent? */
469 Child
= RtlRightChildAvl(Node
);
472 /* Node has no right child, so use it as parent */
474 return TableInsertAsRight
;
478 /* Node has a right child, find most left grand child */
480 while ((Child
= RtlLeftChildAvl(Node
))) Node
= Child
;
482 return TableInsertAsLeft
;
486 /* Update the upper margin if neccessary */
487 if (Node
->StartingVpn
< HighVpn
) HighVpn
= Node
->StartingVpn
;
489 /* Go to the next lower node */
490 Node
= MiGetPreviousNode(Node
);
493 /* Check if there's enough space before the lowest Vad */
494 LowVpn
= ROUND_UP((ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
, Alignment
) >> PAGE_SHIFT
;
495 if ((HighVpn
> LowVpn
) && ((HighVpn
- LowVpn
) >= PageCount
))
497 /* There is enough space to add our address */
498 LowVpn
= HighVpn
- PageCount
;
499 *Base
= LowVpn
<< PAGE_SHIFT
;
500 *Parent
= LowestNode
;
501 return TableInsertAsLeft
;
504 /* No address space left at all */
507 return TableFoundNode
;
512 MiFindEmptyAddressRangeDownBasedTree(IN SIZE_T Length
,
513 IN ULONG_PTR BoundaryAddress
,
514 IN ULONG_PTR Alignment
,
515 IN PMM_AVL_TABLE Table
,
518 PMMADDRESS_NODE Node
, LowestNode
;
519 ULONG_PTR LowVpn
, BestVpn
;
522 ASSERT(Table
== &MmSectionBasedRoot
);
523 ASSERT(BoundaryAddress
);
524 ASSERT(BoundaryAddress
<= ((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1));
526 /* Compute page length, make sure the boundary address is valid */
527 Length
= ROUND_TO_PAGES(Length
);
528 if ((BoundaryAddress
+ 1) < Length
) return STATUS_NO_MEMORY
;
530 /* Check if the table is empty */
531 BestVpn
= ROUND_UP(BoundaryAddress
+ 1 - Length
, Alignment
);
532 if (Table
->NumberGenericTableElements
== 0)
534 /* Tree is empty, the candidate address is already the best one */
536 return STATUS_SUCCESS
;
539 /* Go to the right-most node which should be the biggest address */
540 Node
= Table
->BalancedRoot
.RightChild
;
541 while (RtlRightChildAvl(Node
)) Node
= RtlRightChildAvl(Node
);
543 /* Check if we can fit in here */
544 LowVpn
= ROUND_UP(Node
->EndingVpn
, Alignment
);
545 if ((LowVpn
< BoundaryAddress
) && (Length
< (BoundaryAddress
- LowVpn
)))
547 /* Return the address */
548 *Base
= ROUND_UP(BoundaryAddress
- Length
, Alignment
);
549 return STATUS_SUCCESS
;
552 /* Now loop the Vad nodes */
555 /* Break out if we've reached the last node */
556 LowestNode
= MiGetPreviousNode(Node
);
557 if (!LowestNode
) break;
559 /* Check if this node could contain the requested address */
560 LowVpn
= ROUND_UP(LowestNode
->EndingVpn
+ 1, Alignment
);
561 if ((LowestNode
->EndingVpn
< BestVpn
) &&
562 (Length
<= (Node
->StartingVpn
- LowVpn
)))
564 /* Check if it fits in perfectly */
565 if ((BestVpn
> LowestNode
->EndingVpn
) &&
566 (BoundaryAddress
< Node
->StartingVpn
))
568 /* Return the optimal VPN address */
570 return STATUS_SUCCESS
;
573 /* It doesn't, check if it can partly fit */
574 if (Node
->StartingVpn
> LowVpn
)
576 /* Return an aligned base address within this node */
577 *Base
= ROUND_UP(Node
->StartingVpn
- Length
, Alignment
);
578 return STATUS_SUCCESS
;
582 /* Move to the next node */
586 /* Check if there's enough space before the lowest Vad */
587 if ((Node
->StartingVpn
> (ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
) &&
588 ((Node
->StartingVpn
- (ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
) > Length
))
590 /* Check if it fits in perfectly */
591 if (BoundaryAddress
< Node
->StartingVpn
)
593 /* Return the optimal VPN address */
595 return STATUS_SUCCESS
;
598 /* Return an aligned base address within this node */
599 *Base
= ROUND_UP(Node
->StartingVpn
- Length
, Alignment
);
600 return STATUS_SUCCESS
;
603 /* No address space left at all */
604 return STATUS_NO_MEMORY
;
609 MiCheckSecuredVad(IN PMMVAD Vad
,
612 IN ULONG ProtectionMask
)
614 ULONG_PTR StartAddress
, EndAddress
;
616 /* Compute start and end address */
617 StartAddress
= (ULONG_PTR
)Base
;
618 EndAddress
= StartAddress
+ Size
- 1;
620 /* Are we deleting/unmapping, or changing? */
621 if (ProtectionMask
< MM_DELETE_CHECK
)
623 /* Changing... are we allowed to do so? */
624 if ((Vad
->u
.VadFlags
.NoChange
== 1) &&
625 (Vad
->u2
.VadFlags2
.SecNoChange
== 1) &&
626 (Vad
->u
.VadFlags
.Protection
!= ProtectionMask
))
629 DPRINT1("Trying to mess with a no-change VAD!\n");
630 return STATUS_INVALID_PAGE_PROTECTION
;
635 /* This is allowed */
639 /* ARM3 doesn't support this yet */
640 ASSERT(Vad
->u2
.VadFlags2
.MultipleSecured
== 0);
642 /* Is this a one-secured VAD, like a TEB or PEB? */
643 if (Vad
->u2
.VadFlags2
.OneSecured
)
645 /* Is this allocation being described by the VAD? */
646 if ((StartAddress
<= ((PMMVAD_LONG
)Vad
)->u3
.Secured
.EndVpn
) &&
647 (EndAddress
>= ((PMMVAD_LONG
)Vad
)->u3
.Secured
.StartVpn
))
650 if (ProtectionMask
&& MM_DECOMMIT
)
652 DPRINT1("Not allowed to change protection on guard page!\n");
653 return STATUS_INVALID_PAGE_PROTECTION
;
656 /* ARM3 doesn't have read-only VADs yet */
657 ASSERT(Vad
->u2
.VadFlags2
.ReadOnly
== 0);
659 /* Check if read-write protections are allowed */
660 if (MmReadWrite
[ProtectionMask
] < MM_READ_WRITE_ALLOWED
)
662 DPRINT1("Invalid protection mask for RW access!\n");
663 return STATUS_INVALID_PAGE_PROTECTION
;
668 /* All good, allow the change */
669 return STATUS_SUCCESS
;