- Removed the process from the parameter list of MmCreateMemoryArea.
[reactos.git] / reactos / ntoskrnl / mm / marea.c
1 /* $Id$
2 *
3 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 *
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/marea.c
22 * PURPOSE: Implements memory areas
23 *
24 * PROGRAMMERS: Rex Jolliff
25 * David Welch
26 * Eric Kohl
27 * Philip Susi
28 * Casper Hornstrup
29 * Hartmut Birr
30 * Eric Kohl
31 * Ge van Geldorp
32 * Royce Mitchell III
33 * Aleksey Bragin
34 * Jason Filby
35 * Thomas Weidenmueller
36 * Gunnar Andre' Dalsnes
37 * Mike Nordell
38 * Alex Ionescu
39 * Filip Navara
40 * Herve Poussineau
41 * Steven Edwards
42 */
43
44 /* INCLUDES *****************************************************************/
45
46 #include <ntoskrnl.h>
47 #define NDEBUG
48 #include <internal/debug.h>
49
50 /* #define VALIDATE_MEMORY_AREAS */
51
52 /* FUNCTIONS *****************************************************************/
53
54 /**
55 * @name MmIterateFirstNode
56 *
57 * @param Node
58 * Head node of the MEMORY_AREA tree.
59 *
60 * @return The leftmost MEMORY_AREA node (ie. the one with lowest
61 * address)
62 */
63
64 static PMEMORY_AREA MmIterateFirstNode(PMEMORY_AREA Node)
65 {
66 while (Node->LeftChild != NULL)
67 Node = Node->LeftChild;
68
69 return Node;
70 }
71
72 /**
73 * @name MmIterateNextNode
74 *
75 * @param Node
76 * Current node in the tree.
77 *
78 * @return Next node in the tree (sorted by address).
79 */
80
81 static PMEMORY_AREA MmIterateNextNode(PMEMORY_AREA Node)
82 {
83 if (Node->RightChild != NULL)
84 {
85 Node = Node->RightChild;
86 while (Node->LeftChild != NULL)
87 Node = Node->LeftChild;
88 }
89 else
90 {
91 PMEMORY_AREA TempNode = NULL;
92
93 do
94 {
95 /* Check if we're at the end of tree. */
96 if (Node->Parent == NULL)
97 return NULL;
98
99 TempNode = Node;
100 Node = Node->Parent;
101 }
102 while (TempNode == Node->RightChild);
103 }
104 return Node;
105 }
106
107 /**
108 * @name MmIterateLastNode
109 *
110 * @param Node
111 * Head node of the MEMORY_AREA tree.
112 *
113 * @return The rightmost MEMORY_AREA node (ie. the one with highest
114 * address)
115 */
116
117 static PMEMORY_AREA MmIterateLastNode(PMEMORY_AREA Node)
118 {
119 while (Node->RightChild != NULL)
120 Node = Node->RightChild;
121
122 return Node;
123 }
124
125 /**
126 * @name MmIteratePreviousNode
127 *
128 * @param Node
129 * Current node in the tree.
130 *
131 * @return Previous node in the tree (sorted by address).
132 */
133
134 static PMEMORY_AREA MmIteratePrevNode(PMEMORY_AREA Node)
135 {
136 if (Node->LeftChild != NULL)
137 {
138 Node = Node->LeftChild;
139 while (Node->RightChild != NULL)
140 Node = Node->RightChild;
141 }
142 else
143 {
144 PMEMORY_AREA TempNode = NULL;
145
146 do
147 {
148 /* Check if we're at the end of tree. */
149 if (Node->Parent == NULL)
150 return NULL;
151
152 TempNode = Node;
153 Node = Node->Parent;
154 }
155 while (TempNode == Node->LeftChild);
156 }
157 return Node;
158 }
159
160 #ifdef VALIDATE_MEMORY_AREAS
161 static VOID MmVerifyMemoryAreas(PMADDRESS_SPACE AddressSpace)
162 {
163 PMEMORY_AREA Node;
164
165 ASSERT(AddressSpace != NULL);
166
167 /* Special case for empty tree. */
168 if (AddressSpace->MemoryAreaRoot == NULL)
169 return;
170
171 /* Traverse the tree from left to right. */
172 for (Node = MmIterateFirstNode(AddressSpace->MemoryAreaRoot);
173 Node != NULL;
174 Node = MmIterateNextNode(Node))
175 {
176 /* FiN: The starting address can be NULL if someone explicitely asks
177 * for NULL address. */
178 ASSERT(Node->StartingAddress >= AddressSpace->LowestAddress ||
179 Node->StartingAddress == NULL);
180 ASSERT(Node->EndingAddress >= Node->StartingAddress);
181 }
182 }
183 #else
184 #define MmVerifyMemoryAreas(x)
185 #endif
186
187 VOID STDCALL
188 MmDumpMemoryAreas(PMADDRESS_SPACE AddressSpace)
189 {
190 PMEMORY_AREA Node;
191
192 DbgPrint("MmDumpMemoryAreas()\n");
193
194 /* Special case for empty tree. */
195 if (AddressSpace->MemoryAreaRoot == NULL)
196 return;
197
198 /* Traverse the tree from left to right. */
199 for (Node = MmIterateFirstNode(AddressSpace->MemoryAreaRoot);
200 Node != NULL;
201 Node = MmIterateNextNode(Node))
202 {
203 DbgPrint("Start %p End %p Protect %x Flags %x\n",
204 Node->StartingAddress, Node->EndingAddress,
205 Node->Protect, Node->Flags);
206 }
207
208 DbgPrint("Finished MmDumpMemoryAreas()\n");
209 }
210
211 PMEMORY_AREA STDCALL
212 MmLocateMemoryAreaByAddress(
213 PMADDRESS_SPACE AddressSpace,
214 PVOID Address)
215 {
216 PMEMORY_AREA Node = AddressSpace->MemoryAreaRoot;
217
218 DPRINT("MmLocateMemoryAreaByAddress(AddressSpace %p, Address %p)\n",
219 AddressSpace, Address);
220
221 MmVerifyMemoryAreas(AddressSpace);
222
223 while (Node != NULL)
224 {
225 if (Address < Node->StartingAddress)
226 Node = Node->LeftChild;
227 else if (Address >= Node->EndingAddress)
228 Node = Node->RightChild;
229 else
230 {
231 DPRINT("MmLocateMemoryAreaByAddress(%p): %p [%p - %p]\n",
232 Address, Node, Node->StartingAddress, Node->EndingAddress);
233 return Node;
234 }
235 }
236
237 DPRINT("MmLocateMemoryAreaByAddress(%p): 0\n", Address);
238 return NULL;
239 }
240
241 PMEMORY_AREA STDCALL
242 MmLocateMemoryAreaByRegion(
243 PMADDRESS_SPACE AddressSpace,
244 PVOID Address,
245 ULONG_PTR Length)
246 {
247 PMEMORY_AREA Node;
248 PVOID Extent = (PVOID)((ULONG_PTR)Address + Length);
249
250 MmVerifyMemoryAreas(AddressSpace);
251
252 /* Special case for empty tree. */
253 if (AddressSpace->MemoryAreaRoot == NULL)
254 return NULL;
255
256 /* Traverse the tree from left to right. */
257 for (Node = MmIterateFirstNode(AddressSpace->MemoryAreaRoot);
258 Node != NULL;
259 Node = MmIterateNextNode(Node))
260 {
261 if (Node->StartingAddress >= Address &&
262 Node->StartingAddress < Extent)
263 {
264 DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
265 Address, (ULONG_PTR)Address + Length, Node->StartingAddress,
266 Node->EndingAddress);
267 return Node;
268 }
269 if (Node->EndingAddress > Address &&
270 Node->EndingAddress < Extent)
271 {
272 DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
273 Address, (ULONG_PTR)Address + Length, Node->StartingAddress,
274 Node->EndingAddress);
275 return Node;
276 }
277 if (Node->StartingAddress <= Address &&
278 Node->EndingAddress >= Extent)
279 {
280 DPRINT("MmLocateMemoryAreaByRegion(%p - %p): %p - %p\n",
281 Address, (ULONG_PTR)Address + Length, Node->StartingAddress,
282 Node->EndingAddress);
283 return Node;
284 }
285 if (Node->StartingAddress >= Extent)
286 {
287 DPRINT("Finished MmLocateMemoryAreaByRegion() = NULL\n");
288 return NULL;
289 }
290 }
291
292 return NULL;
293 }
294
295 /**
296 * @name MmCompressHelper
297 *
298 * This is helper of MmRebalanceTree. Performs a compression transformation
299 * count times, starting at root.
300 */
301
302 static VOID
303 MmCompressHelper(
304 PMADDRESS_SPACE AddressSpace,
305 ULONG Count)
306 {
307 PMEMORY_AREA Root = NULL;
308 PMEMORY_AREA Red = AddressSpace->MemoryAreaRoot;
309 PMEMORY_AREA Black = Red->LeftChild;
310
311 while (Count--)
312 {
313 if (Root)
314 Root->LeftChild = Black;
315 else
316 AddressSpace->MemoryAreaRoot = Black;
317 Black->Parent = Root;
318 Red->LeftChild = Black->RightChild;
319 if (Black->RightChild)
320 Black->RightChild->Parent = Red;
321 Black->RightChild = Red;
322 Red->Parent = Black;
323 Root = Black;
324
325 if (Count)
326 {
327 Red = Root->LeftChild;
328 Black = Red->LeftChild;
329 }
330 }
331 }
332
333 /**
334 * @name MmRebalanceTree
335 *
336 * Rebalance a memory area tree using the Tree->Vine->Balanced Tree
337 * method described in libavl documentation in chapter 4.12.
338 * (http://www.stanford.edu/~blp/avl/libavl.html/)
339 */
340
341 static VOID
342 MmRebalanceTree(
343 PMADDRESS_SPACE AddressSpace)
344 {
345 PMEMORY_AREA PreviousNode;
346 PMEMORY_AREA CurrentNode;
347 PMEMORY_AREA TempNode;
348 ULONG NodeCount = 0;
349 ULONG Vine; /* Number of nodes in main vine. */
350 ULONG Leaves; /* Nodes in incomplete bottom level, if any. */
351 INT Height; /* Height of produced balanced tree. */
352
353 /* Transform the tree into Vine. */
354
355 PreviousNode = NULL;
356 CurrentNode = AddressSpace->MemoryAreaRoot;
357 while (CurrentNode != NULL)
358 {
359 if (CurrentNode->RightChild == NULL)
360 {
361 PreviousNode = CurrentNode;
362 CurrentNode = CurrentNode->LeftChild;
363 NodeCount++;
364 }
365 else
366 {
367 TempNode = CurrentNode->RightChild;
368
369 CurrentNode->RightChild = TempNode->LeftChild;
370 if (TempNode->LeftChild)
371 TempNode->LeftChild->Parent = CurrentNode;
372
373 TempNode->LeftChild = CurrentNode;
374 CurrentNode->Parent = TempNode;
375
376 CurrentNode = TempNode;
377
378 if (PreviousNode != NULL)
379 PreviousNode->LeftChild = TempNode;
380 else
381 AddressSpace->MemoryAreaRoot = TempNode;
382 TempNode->Parent = PreviousNode;
383 }
384 }
385
386 /* Transform Vine back into a balanced tree. */
387
388 Leaves = NodeCount + 1;
389 for (;;)
390 {
391 ULONG Next = Leaves & (Leaves - 1);
392 if (Next == 0)
393 break;
394 Leaves = Next;
395 }
396 Leaves = NodeCount + 1 - Leaves;
397
398 MmCompressHelper(AddressSpace, Leaves);
399
400 Vine = NodeCount - Leaves;
401 Height = 1 + (Leaves > 0);
402 while (Vine > 1)
403 {
404 MmCompressHelper(AddressSpace, Vine / 2);
405 Vine /= 2;
406 Height++;
407 }
408 }
409
410 static VOID
411 MmInsertMemoryArea(
412 PMADDRESS_SPACE AddressSpace,
413 PMEMORY_AREA marea)
414 {
415 PMEMORY_AREA Node;
416 PMEMORY_AREA PreviousNode;
417 ULONG Depth = 0;
418
419 MmVerifyMemoryAreas(AddressSpace);
420
421 if (AddressSpace->MemoryAreaRoot == NULL)
422 {
423 AddressSpace->MemoryAreaRoot = marea;
424 marea->LeftChild = marea->RightChild = marea->Parent = NULL;
425 return;
426 }
427
428 Node = AddressSpace->MemoryAreaRoot;
429 do
430 {
431 DPRINT("marea->EndingAddress: %p Node->StartingAddress: %p\n",
432 marea->EndingAddress, Node->StartingAddress);
433 DPRINT("marea->StartingAddress: %p Node->EndingAddress: %p\n",
434 marea->StartingAddress, Node->EndingAddress);
435 ASSERT(marea->EndingAddress <= Node->StartingAddress ||
436 marea->StartingAddress >= Node->EndingAddress);
437 ASSERT(marea->StartingAddress != Node->StartingAddress);
438
439 PreviousNode = Node;
440
441 if (marea->StartingAddress < Node->StartingAddress)
442 Node = Node->LeftChild;
443 else
444 Node = Node->RightChild;
445
446 if (Node)
447 {
448 Depth++;
449 if (Depth == 22)
450 {
451 MmRebalanceTree(AddressSpace);
452 PreviousNode = Node->Parent;
453 }
454 }
455 }
456 while (Node != NULL);
457
458 marea->LeftChild = marea->RightChild = NULL;
459 marea->Parent = PreviousNode;
460 if (marea->StartingAddress < PreviousNode->StartingAddress)
461 PreviousNode->LeftChild = marea;
462 else
463 PreviousNode->RightChild = marea;
464 }
465
466 static PVOID
467 MmFindGapBottomUp(
468 PMADDRESS_SPACE AddressSpace,
469 ULONG_PTR Length,
470 ULONG_PTR Granularity)
471 {
472 PVOID HighestAddress = AddressSpace->LowestAddress < MmSystemRangeStart ?
473 (PVOID)((ULONG_PTR)MmSystemRangeStart - 1) : (PVOID)MAXULONG_PTR;
474 PVOID AlignedAddress;
475 PMEMORY_AREA Node;
476 PMEMORY_AREA FirstNode;
477 PMEMORY_AREA PreviousNode;
478
479 MmVerifyMemoryAreas(AddressSpace);
480
481 DPRINT("LowestAddress: %p HighestAddress: %p\n",
482 AddressSpace->LowestAddress, HighestAddress);
483
484 AlignedAddress = MM_ROUND_UP(AddressSpace->LowestAddress, Granularity);
485
486 /* Special case for empty tree. */
487 if (AddressSpace->MemoryAreaRoot == NULL)
488 {
489 if ((ULONG_PTR)HighestAddress - (ULONG_PTR)AlignedAddress >= Length)
490 {
491 DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
492 return AlignedAddress;
493 }
494 DPRINT("MmFindGapBottomUp: 0\n");
495 return 0;
496 }
497
498 /* Go to the node with lowest address in the tree. */
499 FirstNode = Node = MmIterateFirstNode(AddressSpace->MemoryAreaRoot);
500
501 /* Traverse the tree from left to right. */
502 PreviousNode = Node;
503 for (;;)
504 {
505 Node = MmIterateNextNode(Node);
506 if (Node == NULL)
507 break;
508
509 AlignedAddress = MM_ROUND_UP(PreviousNode->EndingAddress, Granularity);
510 if (Node->StartingAddress > AlignedAddress &&
511 (ULONG_PTR)Node->StartingAddress - (ULONG_PTR)AlignedAddress >= Length)
512 {
513 DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
514 return AlignedAddress;
515 }
516
517 PreviousNode = Node;
518 }
519
520 /* Check if there is enough space after the last memory area. */
521 AlignedAddress = MM_ROUND_UP(PreviousNode->EndingAddress, Granularity);
522 if ((ULONG_PTR)HighestAddress > (ULONG_PTR)AlignedAddress &&
523 (ULONG_PTR)HighestAddress - (ULONG_PTR)AlignedAddress >= Length)
524 {
525 DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
526 return AlignedAddress;
527 }
528
529 /* Check if there is enough space before the first memory area. */
530 AlignedAddress = MM_ROUND_UP(AddressSpace->LowestAddress, Granularity);
531 if (FirstNode->StartingAddress > AlignedAddress &&
532 (ULONG_PTR)FirstNode->StartingAddress - (ULONG_PTR)AlignedAddress >= Length)
533 {
534 DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress);
535 return AlignedAddress;
536 }
537
538 DPRINT("MmFindGapBottomUp: 0\n");
539 return 0;
540 }
541
542
543 static PVOID
544 MmFindGapTopDown(
545 PMADDRESS_SPACE AddressSpace,
546 ULONG_PTR Length,
547 ULONG_PTR Granularity)
548 {
549 PVOID HighestAddress = AddressSpace->LowestAddress < MmSystemRangeStart ?
550 (PVOID)((ULONG_PTR)MmSystemRangeStart - 1) : (PVOID)MAXULONG_PTR;
551 PVOID AlignedAddress;
552 PMEMORY_AREA Node;
553 PMEMORY_AREA PreviousNode;
554
555 MmVerifyMemoryAreas(AddressSpace);
556
557 DPRINT("LowestAddress: %p HighestAddress: %p\n",
558 AddressSpace->LowestAddress, HighestAddress);
559
560 AlignedAddress = MM_ROUND_DOWN((ULONG_PTR)HighestAddress - Length + 1, Granularity);
561
562 /* Check for overflow. */
563 if (AlignedAddress > HighestAddress)
564 return NULL;
565
566 /* Special case for empty tree. */
567 if (AddressSpace->MemoryAreaRoot == NULL)
568 {
569 if (AlignedAddress >= (PVOID)AddressSpace->LowestAddress)
570 {
571 DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
572 return AlignedAddress;
573 }
574 DPRINT("MmFindGapTopDown: 0\n");
575 return 0;
576 }
577
578 /* Go to the node with highest address in the tree. */
579 Node = MmIterateLastNode(AddressSpace->MemoryAreaRoot);
580
581 /* Check if there is enough space after the last memory area. */
582 if (Node->EndingAddress <= AlignedAddress)
583 {
584 DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
585 return AlignedAddress;
586 }
587
588 /* Traverse the tree from left to right. */
589 PreviousNode = Node;
590 for (;;)
591 {
592 Node = MmIteratePrevNode(Node);
593 if (Node == NULL)
594 break;
595
596 AlignedAddress = MM_ROUND_DOWN((ULONG_PTR)PreviousNode->StartingAddress - Length + 1, Granularity);
597
598 /* Check for overflow. */
599 if (AlignedAddress > PreviousNode->StartingAddress)
600 return NULL;
601
602 if (Node->EndingAddress <= AlignedAddress)
603 {
604 DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
605 return AlignedAddress;
606 }
607
608 PreviousNode = Node;
609 }
610
611 AlignedAddress = MM_ROUND_DOWN((ULONG_PTR)PreviousNode->StartingAddress - Length + 1, Granularity);
612
613 /* Check for overflow. */
614 if (AlignedAddress > PreviousNode->StartingAddress)
615 return NULL;
616
617 if (AlignedAddress >= (PVOID)AddressSpace->LowestAddress)
618 {
619 DPRINT("MmFindGapTopDown: %p\n", AlignedAddress);
620 return AlignedAddress;
621 }
622
623 DPRINT("MmFindGapTopDown: 0\n");
624 return 0;
625 }
626
627
628 PVOID STDCALL
629 MmFindGap(
630 PMADDRESS_SPACE AddressSpace,
631 ULONG_PTR Length,
632 ULONG_PTR Granularity,
633 BOOLEAN TopDown)
634 {
635 if (TopDown)
636 return MmFindGapTopDown(AddressSpace, Length, Granularity);
637
638 return MmFindGapBottomUp(AddressSpace, Length, Granularity);
639 }
640
641 ULONG_PTR STDCALL
642 MmFindGapAtAddress(
643 PMADDRESS_SPACE AddressSpace,
644 PVOID Address)
645 {
646 PMEMORY_AREA Node = AddressSpace->MemoryAreaRoot;
647 PMEMORY_AREA RightNeighbour = NULL;
648 PVOID HighestAddress = AddressSpace->LowestAddress < MmSystemRangeStart ?
649 (PVOID)((ULONG_PTR)MmSystemRangeStart - 1) : (PVOID)MAXULONG_PTR;
650
651 MmVerifyMemoryAreas(AddressSpace);
652
653 Address = MM_ROUND_DOWN(Address, PAGE_SIZE);
654
655 if (AddressSpace->LowestAddress < MmSystemRangeStart)
656 {
657 if (Address >= MmSystemRangeStart)
658 {
659 return 0;
660 }
661 }
662 else
663 {
664 if (Address < AddressSpace->LowestAddress)
665 {
666 return 0;
667 }
668 }
669
670 while (Node != NULL)
671 {
672 if (Address < Node->StartingAddress)
673 {
674 RightNeighbour = Node;
675 Node = Node->LeftChild;
676 }
677 else if (Address >= Node->EndingAddress)
678 {
679 Node = Node->RightChild;
680 }
681 else
682 {
683 DPRINT("MmFindGapAtAddress: 0\n");
684 return 0;
685 }
686 }
687
688 if (RightNeighbour)
689 {
690 DPRINT("MmFindGapAtAddress: %p [%p]\n", Address,
691 (ULONG_PTR)RightNeighbour->StartingAddress - (ULONG_PTR)Address);
692 return (ULONG_PTR)RightNeighbour->StartingAddress - (ULONG_PTR)Address;
693 }
694 else
695 {
696 DPRINT("MmFindGapAtAddress: %p [%p]\n", Address,
697 (ULONG_PTR)HighestAddress - (ULONG_PTR)Address);
698 return (ULONG_PTR)HighestAddress - (ULONG_PTR)Address;
699 }
700 }
701
702 /**
703 * @name MmInitMemoryAreas
704 *
705 * Initialize the memory area list implementation.
706 */
707
708 NTSTATUS
709 INIT_FUNCTION
710 NTAPI
711 MmInitMemoryAreas(VOID)
712 {
713 DPRINT("MmInitMemoryAreas()\n",0);
714 return(STATUS_SUCCESS);
715 }
716
717
718 /**
719 * @name MmFreeMemoryArea
720 *
721 * Free an existing memory area.
722 *
723 * @param AddressSpace
724 * Address space to free the area from.
725 * @param MemoryArea
726 * Memory area we're about to free.
727 * @param FreePage
728 * Callback function for each freed page.
729 * @param FreePageContext
730 * Context passed to the callback function.
731 *
732 * @return Status
733 *
734 * @remarks Lock the address space before calling this function.
735 */
736
737 NTSTATUS STDCALL
738 MmFreeMemoryArea(
739 PMADDRESS_SPACE AddressSpace,
740 PMEMORY_AREA MemoryArea,
741 PMM_FREE_PAGE_FUNC FreePage,
742 PVOID FreePageContext)
743 {
744 PMEMORY_AREA *ParentReplace;
745 ULONG_PTR Address;
746 PVOID EndAddress;
747 PEPROCESS CurrentProcess = PsGetCurrentProcess();
748
749 if (AddressSpace->Process != NULL &&
750 AddressSpace->Process != CurrentProcess)
751 {
752 KeAttachProcess(&AddressSpace->Process->Pcb);
753 }
754
755 EndAddress = MM_ROUND_UP(MemoryArea->EndingAddress, PAGE_SIZE);
756 for (Address = (ULONG_PTR)MemoryArea->StartingAddress;
757 Address < (ULONG_PTR)EndAddress;
758 Address += PAGE_SIZE)
759 {
760 if (MemoryArea->Type == MEMORY_AREA_IO_MAPPING)
761 {
762 MmRawDeleteVirtualMapping((PVOID)Address);
763 }
764 else
765 {
766 BOOL Dirty = FALSE;
767 SWAPENTRY SwapEntry = 0;
768 PFN_TYPE Page = 0;
769
770 if (MmIsPageSwapEntry(AddressSpace->Process, (PVOID)Address))
771 {
772 MmDeletePageFileMapping(AddressSpace->Process, (PVOID)Address, &SwapEntry);
773 }
774 else
775 {
776 MmDeleteVirtualMapping(AddressSpace->Process, (PVOID)Address, FALSE, &Dirty, &Page);
777 }
778 if (FreePage != NULL)
779 {
780 FreePage(FreePageContext, MemoryArea, (PVOID)Address,
781 Page, SwapEntry, (BOOLEAN)Dirty);
782 }
783 }
784 }
785
786 if (AddressSpace->Process != NULL &&
787 AddressSpace->Process != CurrentProcess)
788 {
789 KeDetachProcess();
790 }
791
792 /* Remove the tree item. */
793 {
794 if (MemoryArea->Parent != NULL)
795 {
796 if (MemoryArea->Parent->LeftChild == MemoryArea)
797 ParentReplace = &MemoryArea->Parent->LeftChild;
798 else
799 ParentReplace = &MemoryArea->Parent->RightChild;
800 }
801 else
802 ParentReplace = &AddressSpace->MemoryAreaRoot;
803
804 if (MemoryArea->RightChild == NULL)
805 {
806 *ParentReplace = MemoryArea->LeftChild;
807 if (MemoryArea->LeftChild)
808 MemoryArea->LeftChild->Parent = MemoryArea->Parent;
809 }
810 else
811 {
812 if (MemoryArea->RightChild->LeftChild == NULL)
813 {
814 MemoryArea->RightChild->LeftChild = MemoryArea->LeftChild;
815 if (MemoryArea->LeftChild)
816 MemoryArea->LeftChild->Parent = MemoryArea->RightChild;
817
818 *ParentReplace = MemoryArea->RightChild;
819 MemoryArea->RightChild->Parent = MemoryArea->Parent;
820 }
821 else
822 {
823 PMEMORY_AREA LowestNode;
824
825 LowestNode = MemoryArea->RightChild->LeftChild;
826 while (LowestNode->LeftChild != NULL)
827 LowestNode = LowestNode->LeftChild;
828
829 LowestNode->Parent->LeftChild = LowestNode->RightChild;
830 if (LowestNode->RightChild)
831 LowestNode->RightChild->Parent = LowestNode->Parent;
832
833 LowestNode->LeftChild = MemoryArea->LeftChild;
834 if (MemoryArea->LeftChild)
835 MemoryArea->LeftChild->Parent = LowestNode;
836
837 LowestNode->RightChild = MemoryArea->RightChild;
838 MemoryArea->RightChild->Parent = LowestNode;
839
840 *ParentReplace = LowestNode;
841 LowestNode->Parent = MemoryArea->Parent;
842 }
843 }
844 }
845
846 ExFreePoolWithTag(MemoryArea, TAG_MAREA);
847
848 DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
849
850 return STATUS_SUCCESS;
851 }
852
853 /**
854 * @name MmFreeMemoryAreaByPtr
855 *
856 * Free an existing memory area given a pointer inside it.
857 *
858 * @param AddressSpace
859 * Address space to free the area from.
860 * @param BaseAddress
861 * Address in the memory area we're about to free.
862 * @param FreePage
863 * Callback function for each freed page.
864 * @param FreePageContext
865 * Context passed to the callback function.
866 *
867 * @return Status
868 *
869 * @see MmFreeMemoryArea
870 *
871 * @todo Should we require the BaseAddress to be really the starting
872 * address of the memory area or is the current relaxed check
873 * (BaseAddress can point anywhere in the memory area) acceptable?
874 *
875 * @remarks Lock the address space before calling this function.
876 */
877
878 NTSTATUS STDCALL
879 MmFreeMemoryAreaByPtr(
880 PMADDRESS_SPACE AddressSpace,
881 PVOID BaseAddress,
882 PMM_FREE_PAGE_FUNC FreePage,
883 PVOID FreePageContext)
884 {
885 PMEMORY_AREA MemoryArea;
886
887 DPRINT("MmFreeMemoryArea(AddressSpace %p, BaseAddress %p, "
888 "FreePageContext %p)\n", AddressSpace, BaseAddress,
889 FreePageContext);
890
891 MmVerifyMemoryAreas(AddressSpace);
892
893 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
894 BaseAddress);
895 if (MemoryArea == NULL)
896 {
897 KEBUGCHECK(0);
898 return(STATUS_UNSUCCESSFUL);
899 }
900
901 return MmFreeMemoryArea(AddressSpace, MemoryArea, FreePage, FreePageContext);
902 }
903
904 /**
905 * @name MmCreateMemoryArea
906 *
907 * Create a memory area.
908 *
909 * @param AddressSpace
910 * Address space to create the area in.
911 * @param Type
912 * Type of the memory area.
913 * @param BaseAddress
914 * Base address for the memory area we're about the create. On
915 * input it contains either 0 (auto-assign address) or preferred
916 * address. On output it contains the starting address of the
917 * newly created area.
918 * @param Length
919 * Length of the area to allocate.
920 * @param Attributes
921 * Protection attributes for the memory area.
922 * @param Result
923 * Receives a pointer to the memory area on successful exit.
924 *
925 * @return Status
926 *
927 * @remarks Lock the address space before calling this function.
928 */
929
930 NTSTATUS STDCALL
931 MmCreateMemoryArea(PMADDRESS_SPACE AddressSpace,
932 ULONG Type,
933 PVOID *BaseAddress,
934 ULONG_PTR Length,
935 ULONG Protect,
936 PMEMORY_AREA *Result,
937 BOOLEAN FixedAddress,
938 ULONG AllocationFlags,
939 PHYSICAL_ADDRESS BoundaryAddressMultiple)
940 {
941 PVOID EndAddress;
942 ULONG Granularity;
943 ULONG tmpLength;
944 PMEMORY_AREA MemoryArea;
945
946 DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %p, "
947 "*BaseAddress %p, Length %p, Attributes %x, TopDown: %x, "
948 "FixedAddress %x, Result %p)\n",
949 Type, BaseAddress, *BaseAddress, Length, Attributes, TopDown,
950 FixedAddress, Result);
951
952 MmVerifyMemoryAreas(AddressSpace);
953
954 Granularity = (MEMORY_AREA_VIRTUAL_MEMORY == Type ? MM_VIRTMEM_GRANULARITY : PAGE_SIZE);
955 if ((*BaseAddress) == 0 && !FixedAddress)
956 {
957 tmpLength = PAGE_ROUND_UP(Length);
958 *BaseAddress = MmFindGap(AddressSpace,
959 tmpLength,
960 Granularity,
961 (AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN);
962 if ((*BaseAddress) == 0)
963 {
964 DPRINT("No suitable gap\n");
965 return STATUS_NO_MEMORY;
966 }
967 }
968 else
969 {
970 tmpLength = Length + ((ULONG_PTR) *BaseAddress
971 - (ULONG_PTR) MM_ROUND_DOWN(*BaseAddress, Granularity));
972 *BaseAddress = MM_ROUND_DOWN(*BaseAddress, Granularity);
973
974 if (AddressSpace->LowestAddress == MmSystemRangeStart &&
975 *BaseAddress < MmSystemRangeStart)
976 {
977 CHECKPOINT;
978 return STATUS_ACCESS_VIOLATION;
979 }
980
981 if (AddressSpace->LowestAddress < MmSystemRangeStart &&
982 (ULONG_PTR)(*BaseAddress) + tmpLength > (ULONG_PTR)MmSystemRangeStart)
983 {
984 CHECKPOINT;
985 return STATUS_ACCESS_VIOLATION;
986 }
987
988 if (BoundaryAddressMultiple.QuadPart != 0)
989 {
990 EndAddress = ((char*)(*BaseAddress)) + tmpLength-1;
991 ASSERT(((ULONG_PTR)*BaseAddress/BoundaryAddressMultiple.QuadPart) == ((DWORD_PTR)EndAddress/BoundaryAddressMultiple.QuadPart));
992 }
993
994 if (MmLocateMemoryAreaByRegion(AddressSpace,
995 *BaseAddress,
996 tmpLength) != NULL)
997 {
998 DPRINT("Memory area already occupied\n");
999 return STATUS_CONFLICTING_ADDRESSES;
1000 }
1001 }
1002
1003 MemoryArea = ExAllocatePoolWithTag(NonPagedPool, sizeof(MEMORY_AREA),
1004 TAG_MAREA);
1005 RtlZeroMemory(MemoryArea, sizeof(MEMORY_AREA));
1006 MemoryArea->Type = Type;
1007 MemoryArea->StartingAddress = *BaseAddress;
1008 MemoryArea->EndingAddress = (PVOID)((ULONG_PTR)*BaseAddress + tmpLength);
1009 MemoryArea->Protect = Protect;
1010 MemoryArea->Flags = AllocationFlags;
1011 MemoryArea->LockCount = 0;
1012 MemoryArea->PageOpCount = 0;
1013 MemoryArea->DeleteInProgress = FALSE;
1014
1015 MmInsertMemoryArea(AddressSpace, MemoryArea);
1016
1017 *Result = MemoryArea;
1018
1019 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress);
1020 return STATUS_SUCCESS;
1021 }
1022
1023
1024 VOID STDCALL
1025 MmReleaseMemoryAreaIfDecommitted(PEPROCESS Process,
1026 PMADDRESS_SPACE AddressSpace,
1027 PVOID BaseAddress)
1028 {
1029 PMEMORY_AREA MemoryArea;
1030 PLIST_ENTRY Entry;
1031 PMM_REGION Region;
1032 BOOLEAN Reserved;
1033
1034 MmVerifyMemoryAreas(AddressSpace);
1035
1036 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1037 if (MemoryArea != NULL)
1038 {
1039 Entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
1040 Reserved = TRUE;
1041 while (Reserved && Entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
1042 {
1043 Region = CONTAINING_RECORD(Entry, MM_REGION, RegionListEntry);
1044 Reserved = (MEM_RESERVE == Region->Type);
1045 Entry = Entry->Flink;
1046 }
1047
1048 if (Reserved)
1049 {
1050 MmFreeVirtualMemory(Process, MemoryArea);
1051 }
1052 }
1053 }
1054
1055 /* EOF */