Sync with trunk r63935.
[reactos.git] / ntoskrnl / mm / ARM3 / vadnode.c
1 /*
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)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 /* Include Mm version of AVL support */
20 #include "../ARM3/miavl.h"
21 #include "../../../lib/rtl/avlsupp.c"
22
23 /* GLOBALS ********************************************************************/
24
25 CHAR MmReadWrite[32] =
26 {
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,
30
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,
34
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,
38
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,
42 };
43
44 /* FUNCTIONS ******************************************************************/
45
46 PMMVAD
47 NTAPI
48 MiLocateAddress(IN PVOID VirtualAddress)
49 {
50 PMMVAD FoundVad;
51 ULONG_PTR Vpn;
52 PMM_AVL_TABLE Table = &PsGetCurrentProcess()->VadRoot;
53 TABLE_SEARCH_RESULT SearchResult;
54
55 /* Start with the the hint */
56 FoundVad = (PMMVAD)Table->NodeHint;
57 if (!FoundVad) return NULL;
58
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;
62
63 /* VAD hint didn't work, go look for it */
64 SearchResult = RtlpFindAvlTableNodeOrParent(Table,
65 (PVOID)Vpn,
66 (PMMADDRESS_NODE*)&FoundVad);
67 if (SearchResult != TableFoundNode) return NULL;
68
69 /* We found it, update the hint */
70 ASSERT(FoundVad != NULL);
71 ASSERT((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn));
72 Table->NodeHint = FoundVad;
73 return FoundVad;
74 }
75
76 TABLE_SEARCH_RESULT
77 NTAPI
78 MiCheckForConflictingNode(IN ULONG_PTR StartVpn,
79 IN ULONG_PTR EndVpn,
80 IN PMM_AVL_TABLE Table,
81 OUT PMMADDRESS_NODE *NodeOrParent)
82 {
83 PMMADDRESS_NODE ParentNode, CurrentNode;
84
85 /* If the tree is empty, there is no conflict */
86 if (Table->NumberGenericTableElements == 0) return TableEmptyTree;
87
88 /* Start looping from the root node */
89 CurrentNode = RtlRightChildAvl(&Table->BalancedRoot);
90 ASSERT(CurrentNode != NULL);
91 while (CurrentNode)
92 {
93 ParentNode = CurrentNode;
94
95 /* This address comes after */
96 if (StartVpn > CurrentNode->EndingVpn)
97 {
98 /* Keep searching on the right */
99 CurrentNode = RtlRightChildAvl(CurrentNode);
100 }
101 else if (EndVpn < CurrentNode->StartingVpn)
102 {
103 /* This address ends before the node starts, search on the left */
104 CurrentNode = RtlLeftChildAvl(CurrentNode);
105 }
106 else
107 {
108 /* This address is part of this node, return it */
109 *NodeOrParent = ParentNode;
110 return TableFoundNode;
111 }
112 }
113
114 /* There is no more child, save the current node as parent */
115 *NodeOrParent = ParentNode;
116 if (StartVpn > ParentNode->EndingVpn)
117 {
118 return TableInsertAsRight;
119 }
120 else
121 {
122 return TableInsertAsLeft;
123 }
124 }
125
126 VOID
127 NTAPI
128 MiInsertNode(IN PMM_AVL_TABLE Table,
129 IN PMMADDRESS_NODE NewNode,
130 IN PMMADDRESS_NODE Parent,
131 IN TABLE_SEARCH_RESULT Result)
132 {
133 PMMVAD_LONG Vad;
134
135 /* Insert it into the tree */
136 RtlpInsertAvlTreeNode(Table, NewNode, Parent, Result);
137
138 /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */
139 Vad = (PMMVAD_LONG)NewNode;
140 if (Vad->u.VadFlags.Spare == 0)
141 {
142 NTSTATUS Status;
143 PMEMORY_AREA MemoryArea;
144 SIZE_T Size;
145 PEPROCESS Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
146 PVOID AllocatedBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
147
148 Size = ((Vad->EndingVpn + 1) - Vad->StartingVpn) << PAGE_SHIFT;
149 Status = MmCreateMemoryArea(&Process->Vm,
150 MEMORY_AREA_OWNED_BY_ARM3,
151 &AllocatedBase,
152 Size,
153 PAGE_READWRITE,
154 &MemoryArea,
155 TRUE,
156 0,
157 PAGE_SIZE);
158 ASSERT(NT_SUCCESS(Status));
159
160 /* Check if this is VM VAD */
161 if (Vad->ControlArea == NULL)
162 {
163 /* We store the reactos MEMORY_AREA here */
164 Vad->FirstPrototypePte = (PMMPTE)MemoryArea;
165 }
166 else
167 {
168 /* This is a section VAD. Store the MAREA here for now */
169 ASSERT(Vad->u4.Banked == (PVOID)0xDEADBABE);
170 Vad->u4.Banked = (PVOID)MemoryArea;
171 }
172 }
173 }
174
175 VOID
176 NTAPI
177 MiInsertVad(IN PMMVAD Vad,
178 IN PEPROCESS Process)
179 {
180 TABLE_SEARCH_RESULT Result;
181 PMMADDRESS_NODE Parent = NULL;
182
183 /* Validate the VAD and set it as the current hint */
184 ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
185 Process->VadRoot.NodeHint = Vad;
186
187 /* Find the parent VAD and where this child should be inserted */
188 Result = RtlpFindAvlTableNodeOrParent(&Process->VadRoot, (PVOID)Vad->StartingVpn, &Parent);
189 ASSERT(Result != TableFoundNode);
190 ASSERT((Parent != NULL) || (Result == TableEmptyTree));
191
192 /* Do the actual insert operation */
193 MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
194 }
195
196 VOID
197 NTAPI
198 MiInsertBasedSection(IN PSECTION Section)
199 {
200 TABLE_SEARCH_RESULT Result;
201 PMMADDRESS_NODE Parent = NULL;
202 ASSERT(Section->Address.EndingVpn >= Section->Address.StartingVpn);
203
204 /* Find the parent VAD and where this child should be inserted */
205 Result = RtlpFindAvlTableNodeOrParent(&MmSectionBasedRoot, (PVOID)Section->Address.StartingVpn, &Parent);
206 ASSERT(Result != TableFoundNode);
207 ASSERT((Parent != NULL) || (Result == TableEmptyTree));
208 MiInsertNode(&MmSectionBasedRoot, &Section->Address, Parent, Result);
209 }
210
211 VOID
212 NTAPI
213 MiRemoveNode(IN PMMADDRESS_NODE Node,
214 IN PMM_AVL_TABLE Table)
215 {
216 PMMVAD_LONG Vad;
217
218 /* Call the AVL code */
219 RtlpDeleteAvlTreeNode(Table, Node);
220
221 /* Decrease element count */
222 Table->NumberGenericTableElements--;
223
224 /* Check if this node was the hint */
225 if (Table->NodeHint == Node)
226 {
227 /* Get a new hint, unless we're empty now, in which case nothing */
228 if (!Table->NumberGenericTableElements) Table->NodeHint = NULL;
229 else Table->NodeHint = Table->BalancedRoot.RightChild;
230 }
231
232 /* Free the node from ReactOS view as well */
233 Vad = (PMMVAD_LONG)Node;
234 if (Vad->u.VadFlags.Spare == 0)
235 {
236 PMEMORY_AREA MemoryArea;
237 PEPROCESS Process;
238
239 /* Check if this is VM VAD */
240 if (Vad->ControlArea == NULL)
241 {
242 /* We store the ReactOS MEMORY_AREA here */
243 MemoryArea = (PMEMORY_AREA)Vad->FirstPrototypePte;
244 }
245 else
246 {
247 /* This is a section VAD. We store the ReactOS MEMORY_AREA here */
248 MemoryArea = (PMEMORY_AREA)Vad->u4.Banked;
249 }
250
251 /* Make sure one actually still exists */
252 if (MemoryArea)
253 {
254 /* Make sure we have not already freed it */
255 ASSERT(MemoryArea != (PVOID)0xDEADBAB1);
256
257 /* Get the process */
258 Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot);
259
260 /* We only create fake memory-areas for ARM3 VADs */
261 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
262 ASSERT(MemoryArea->Vad == NULL);
263
264 /* Free it */
265 MmFreeMemoryArea(&Process->Vm, MemoryArea, NULL, NULL);
266
267 /* Check if this is VM VAD */
268 if (Vad->ControlArea == NULL)
269 {
270 /* Delete the pointer to it */
271 Vad->FirstPrototypePte = (PVOID)0xDEADBAB1;
272 }
273 else
274 {
275 /* Delete the pointer to it */
276 Vad->u4.Banked = (PVOID)0xDEADBAB1;
277 }
278 }
279 }
280 }
281
282 PMMADDRESS_NODE
283 NTAPI
284 MiGetPreviousNode(IN PMMADDRESS_NODE Node)
285 {
286 PMMADDRESS_NODE Parent;
287
288 /* Get the left child */
289 if (RtlLeftChildAvl(Node))
290 {
291 /* Get right-most child */
292 Node = RtlLeftChildAvl(Node);
293 while (RtlRightChildAvl(Node)) Node = RtlRightChildAvl(Node);
294 return Node;
295 }
296
297 Parent = RtlParentAvl(Node);
298 ASSERT(Parent != NULL);
299 while (Parent != Node)
300 {
301 /* The parent should be a right child, return the real predecessor */
302 if (RtlIsRightChildAvl(Node))
303 {
304 /* Return it unless it's the root */
305 if (Parent == RtlParentAvl(Parent)) Parent = NULL;
306 return Parent;
307 }
308
309 /* Keep lopping until we find our parent */
310 Node = Parent;
311 Parent = RtlParentAvl(Node);
312 }
313
314 /* Nothing found */
315 return NULL;
316 }
317
318 PMMADDRESS_NODE
319 NTAPI
320 MiGetNextNode(IN PMMADDRESS_NODE Node)
321 {
322 PMMADDRESS_NODE Parent;
323
324 /* Get the right child */
325 if (RtlRightChildAvl(Node))
326 {
327 /* Get left-most child */
328 Node = RtlRightChildAvl(Node);
329 while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node);
330 return Node;
331 }
332
333 Parent = RtlParentAvl(Node);
334 ASSERT(Parent != NULL);
335 while (Parent != Node)
336 {
337 /* The parent should be a left child, return the real predecessor */
338 if (RtlIsLeftChildAvl(Node))
339 {
340 /* Return it */
341 return Parent;
342 }
343
344 /* Keep lopping until we find our parent */
345 Node = Parent;
346 Parent = RtlParentAvl(Node);
347 }
348
349 /* Nothing found */
350 return NULL;
351 }
352
353 TABLE_SEARCH_RESULT
354 NTAPI
355 MiFindEmptyAddressRangeInTree(IN SIZE_T Length,
356 IN ULONG_PTR Alignment,
357 IN PMM_AVL_TABLE Table,
358 OUT PMMADDRESS_NODE *PreviousVad,
359 OUT PULONG_PTR Base)
360 {
361 PMMADDRESS_NODE Node, PreviousNode;
362 ULONG_PTR PageCount, AlignmentVpn, LowVpn, HighVpn;
363 ASSERT(Length != 0);
364
365 /* Calculate page numbers for the length, alignment, and starting address */
366 PageCount = BYTES_TO_PAGES(Length);
367 AlignmentVpn = Alignment >> PAGE_SHIFT;
368 LowVpn = ALIGN_UP_BY((ULONG_PTR)MM_LOWEST_USER_ADDRESS >> PAGE_SHIFT, AlignmentVpn);
369
370 /* Check if the table is empty */
371 if (Table->NumberGenericTableElements == 0)
372 {
373 /* Tree is empty, the candidate address is already the best one */
374 *Base = LowVpn << PAGE_SHIFT;
375 return TableEmptyTree;
376 }
377
378 /* Otherwise, follow the leftmost child of the right root node's child */
379 Node = RtlRightChildAvl(&Table->BalancedRoot);
380 while (RtlLeftChildAvl(Node)) Node = RtlLeftChildAvl(Node);
381
382 /* Start a search to find a gap */
383 PreviousNode = NULL;
384 while (Node != NULL)
385 {
386 /* Check if the gap below the current node is suitable */
387 if (Node->StartingVpn >= LowVpn + PageCount)
388 {
389 /* There is enough space to add our node */
390 *Base = LowVpn << PAGE_SHIFT;
391
392 /* Can we use the current node as parent? */
393 if (RtlLeftChildAvl(Node) == NULL)
394 {
395 /* Node has no left child, so use it as parent */
396 *PreviousVad = Node;
397 return TableInsertAsLeft;
398 }
399 else
400 {
401 /* Node has a left child, this means that the previous node is
402 the right-most child of it's left child and can be used as
403 the parent. In case we use the space before the left-most
404 node, it's left child must be NULL. */
405 ASSERT(PreviousNode != NULL);
406 ASSERT(RtlRightChildAvl(PreviousNode) == NULL);
407 *PreviousVad = PreviousNode;
408 return TableInsertAsRight;
409 }
410 }
411
412 /* The next candidate is above the current node */
413 if (Node->EndingVpn >= LowVpn)
414 LowVpn = ALIGN_UP_BY(Node->EndingVpn + 1, AlignmentVpn);
415
416 /* Remember the current node and go to the next node */
417 PreviousNode = Node;
418 Node = MiGetNextNode(Node);
419 }
420
421 /* We're up to the highest VAD, will this allocation fit above it? */
422 HighVpn = ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) / PAGE_SIZE;
423 if (HighVpn >= LowVpn + PageCount)
424 {
425 /* Yes! Use this VAD to store the allocation */
426 *PreviousVad = PreviousNode;
427 *Base = LowVpn << PAGE_SHIFT;
428 return TableInsertAsRight;
429 }
430
431 /* Nyet, there's no free address space for this allocation, so we'll fail */
432 return TableFoundNode;
433 }
434
435 TABLE_SEARCH_RESULT
436 NTAPI
437 MiFindEmptyAddressRangeDownTree(IN SIZE_T Length,
438 IN ULONG_PTR BoundaryAddress,
439 IN ULONG_PTR Alignment,
440 IN PMM_AVL_TABLE Table,
441 OUT PULONG_PTR Base,
442 OUT PMMADDRESS_NODE *Parent)
443 {
444 PMMADDRESS_NODE Node, OldNode, Child;
445 ULONG_PTR LowVpn, HighVpn, AlignmentVpn;
446 PFN_NUMBER PageCount;
447
448 /* Sanity checks */
449 ASSERT(BoundaryAddress);
450 ASSERT(BoundaryAddress <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS));
451 ASSERT((Alignment & (PAGE_SIZE - 1)) == 0);
452
453 /* Calculate page numbers for the length and alignment */
454 Length = ROUND_TO_PAGES(Length);
455 PageCount = Length >> PAGE_SHIFT;
456 AlignmentVpn = Alignment / PAGE_SIZE;
457
458 /* Check if there is enough space below the boundary */
459 if ((ALIGN_UP_BY((ULONG_PTR)MM_LOWEST_USER_ADDRESS, Alignment) + Length) >
460 (BoundaryAddress + 1))
461 {
462 return TableFoundNode;
463 }
464
465 /* Check if the table is empty */
466 if (Table->NumberGenericTableElements == 0)
467 {
468 /* Tree is empty, the candidate address is already the best one */
469 *Base = ALIGN_DOWN_BY(BoundaryAddress + 1 - Length, Alignment);
470 return TableEmptyTree;
471 }
472
473 /* Calculate the initial upper margin */
474 HighVpn = (BoundaryAddress + 1) >> PAGE_SHIFT;
475
476 /* Starting from the root, follow the right children until we found a node
477 that ends above the boundary */
478 Node = RtlRightChildAvl(&Table->BalancedRoot);
479 while ((Node->EndingVpn < HighVpn) &&
480 ((Child = RtlRightChildAvl(Node)) != NULL)) Node = Child;
481
482 /* Now loop the Vad nodes */
483 while (Node)
484 {
485 /* Calculate the lower margin */
486 LowVpn = ALIGN_UP_BY(Node->EndingVpn + 1, AlignmentVpn);
487
488 /* Check if the current bounds are suitable */
489 if ((HighVpn > LowVpn) && ((HighVpn - LowVpn) >= PageCount))
490 {
491 /* There is enough space to add our node */
492 LowVpn = ALIGN_DOWN_BY(HighVpn - PageCount, AlignmentVpn);
493 *Base = LowVpn << PAGE_SHIFT;
494
495 /* Can we use the current node as parent? */
496 if (!RtlRightChildAvl(Node))
497 {
498 /* Node has no right child, so use it as parent */
499 *Parent = Node;
500 return TableInsertAsRight;
501 }
502 else
503 {
504 /* Node has a right child, the node we had before is the most
505 left grandchild of that right child, use it as parent. */
506 *Parent = OldNode;
507 return TableInsertAsLeft;
508 }
509 }
510
511 /* Update the upper margin if necessary */
512 if (Node->StartingVpn < HighVpn) HighVpn = Node->StartingVpn;
513
514 /* Remember the current node and go to the previous node */
515 OldNode = Node;
516 Node = MiGetPreviousNode(Node);
517 }
518
519 /* Check if there's enough space before the lowest Vad */
520 LowVpn = ALIGN_UP_BY((ULONG_PTR)MI_LOWEST_VAD_ADDRESS, Alignment) / PAGE_SIZE;
521 if ((HighVpn > LowVpn) && ((HighVpn - LowVpn) >= PageCount))
522 {
523 /* There is enough space to add our address */
524 LowVpn = ALIGN_DOWN_BY(HighVpn - PageCount, Alignment >> PAGE_SHIFT);
525 *Base = LowVpn << PAGE_SHIFT;
526 *Parent = OldNode;
527 return TableInsertAsLeft;
528 }
529
530 /* No address space left at all */
531 *Base = 0;
532 *Parent = NULL;
533 return TableFoundNode;
534 }
535
536 NTSTATUS
537 NTAPI
538 MiFindEmptyAddressRangeDownBasedTree(IN SIZE_T Length,
539 IN ULONG_PTR BoundaryAddress,
540 IN ULONG_PTR Alignment,
541 IN PMM_AVL_TABLE Table,
542 OUT PULONG_PTR Base)
543 {
544 PMMADDRESS_NODE Node, LowestNode;
545 ULONG_PTR LowVpn, BestVpn;
546
547 /* Sanity checks */
548 ASSERT(Table == &MmSectionBasedRoot);
549 ASSERT(BoundaryAddress);
550 ASSERT(BoundaryAddress <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
551
552 /* Compute page length, make sure the boundary address is valid */
553 Length = ROUND_TO_PAGES(Length);
554 if ((BoundaryAddress + 1) < Length) return STATUS_NO_MEMORY;
555
556 /* Check if the table is empty */
557 BestVpn = ROUND_DOWN(BoundaryAddress + 1 - Length, Alignment);
558 if (Table->NumberGenericTableElements == 0)
559 {
560 /* Tree is empty, the candidate address is already the best one */
561 *Base = BestVpn;
562 return STATUS_SUCCESS;
563 }
564
565 /* Go to the right-most node which should be the biggest address */
566 Node = Table->BalancedRoot.RightChild;
567 while (RtlRightChildAvl(Node)) Node = RtlRightChildAvl(Node);
568
569 /* Check if we can fit in here */
570 LowVpn = ROUND_UP(Node->EndingVpn + 1, Alignment);
571 if ((LowVpn < BoundaryAddress) && (Length <= (BoundaryAddress - LowVpn)))
572 {
573 #if (NTDDI_VERSION >= NTDDI_VISTA)
574 /* Return the address. */
575 *Base = BestVpn;
576 #else
577 /* Note: this is a compatibility hack that mimics a bug in the 2k3
578 kernel. It will can waste up to Alignment bytes of memory above
579 the allocation. This bug was fixed in Windows Vista */
580 *Base = ROUND_DOWN(BoundaryAddress - Length, Alignment);
581 #endif
582 return STATUS_SUCCESS;
583 }
584
585 /* Now loop the Vad nodes */
586 do
587 {
588 /* Break out if we've reached the last node */
589 LowestNode = MiGetPreviousNode(Node);
590 if (!LowestNode) break;
591
592 /* Check if this node could contain the requested address */
593 LowVpn = ROUND_UP(LowestNode->EndingVpn + 1, Alignment);
594 if ((LowestNode->EndingVpn < BestVpn) &&
595 (LowVpn < Node->StartingVpn) &&
596 (Length <= (Node->StartingVpn - LowVpn)))
597 {
598 /* Check if we need to take BoundaryAddress into account */
599 if (BoundaryAddress < Node->StartingVpn)
600 {
601 /* Return the optimal VPN address */
602 *Base = BestVpn;
603 return STATUS_SUCCESS;
604 }
605 else
606 {
607 /* The upper margin is given by the Node's starting address */
608 *Base = ROUND_DOWN(Node->StartingVpn - Length, Alignment);
609 return STATUS_SUCCESS;
610 }
611 }
612
613 /* Move to the next node */
614 Node = LowestNode;
615 } while (TRUE);
616
617 /* Check if there's enough space before the lowest Vad */
618 if ((Node->StartingVpn > (ULONG_PTR)MI_LOWEST_VAD_ADDRESS) &&
619 ((Node->StartingVpn - (ULONG_PTR)MI_LOWEST_VAD_ADDRESS) >= Length))
620 {
621 /* Check if it fits in perfectly */
622 if (BoundaryAddress < Node->StartingVpn)
623 {
624 /* Return the optimal VPN address */
625 *Base = BestVpn;
626 return STATUS_SUCCESS;
627 }
628
629 /* Return an aligned base address within this node */
630 *Base = ROUND_DOWN(Node->StartingVpn - Length, Alignment);
631 return STATUS_SUCCESS;
632 }
633
634 /* No address space left at all */
635 return STATUS_NO_MEMORY;
636 }
637
638 NTSTATUS
639 NTAPI
640 MiCheckSecuredVad(IN PMMVAD Vad,
641 IN PVOID Base,
642 IN SIZE_T Size,
643 IN ULONG ProtectionMask)
644 {
645 ULONG_PTR StartAddress, EndAddress;
646
647 /* Compute start and end address */
648 StartAddress = (ULONG_PTR)Base;
649 EndAddress = StartAddress + Size - 1;
650
651 /* Are we deleting/unmapping, or changing? */
652 if (ProtectionMask < MM_DELETE_CHECK)
653 {
654 /* Changing... are we allowed to do so? */
655 if ((Vad->u.VadFlags.NoChange == 1) &&
656 (Vad->u2.VadFlags2.SecNoChange == 1) &&
657 (Vad->u.VadFlags.Protection != ProtectionMask))
658 {
659 /* Nope, bail out */
660 DPRINT1("Trying to mess with a no-change VAD!\n");
661 return STATUS_INVALID_PAGE_PROTECTION;
662 }
663 }
664 else
665 {
666 /* This is allowed */
667 ProtectionMask = 0;
668 }
669
670 /* ARM3 doesn't support this yet */
671 ASSERT(Vad->u2.VadFlags2.MultipleSecured == 0);
672
673 /* Is this a one-secured VAD, like a TEB or PEB? */
674 if (Vad->u2.VadFlags2.OneSecured)
675 {
676 /* Is this allocation being described by the VAD? */
677 if ((StartAddress <= ((PMMVAD_LONG)Vad)->u3.Secured.EndVpn) &&
678 (EndAddress >= ((PMMVAD_LONG)Vad)->u3.Secured.StartVpn))
679 {
680 /* Guard page? */
681 if (ProtectionMask & MM_DECOMMIT)
682 {
683 DPRINT1("Not allowed to change protection on guard page!\n");
684 return STATUS_INVALID_PAGE_PROTECTION;
685 }
686
687 /* ARM3 doesn't have read-only VADs yet */
688 ASSERT(Vad->u2.VadFlags2.ReadOnly == 0);
689
690 /* Check if read-write protections are allowed */
691 if (MmReadWrite[ProtectionMask] < MM_READ_WRITE_ALLOWED)
692 {
693 DPRINT1("Invalid protection mask for RW access!\n");
694 return STATUS_INVALID_PAGE_PROTECTION;
695 }
696 }
697 }
698
699 /* All good, allow the change */
700 return STATUS_SUCCESS;
701 }
702
703 /* EOF */
704