3 * Copyright (C) 1998, 1999, 2000, 2001, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/marea.c
22 * PURPOSE: Implements memory areas
23 * PROGRAMMER: David Welch (welch@mcmail.com)
28 /* INCLUDES *****************************************************************/
32 #include <internal/debug.h>
34 /* GLOBALS *******************************************************************/
36 #define TAG_MAREA TAG('M', 'A', 'R', 'E')
38 /* FUNCTIONS *****************************************************************/
40 VOID
MmDumpMemoryAreas(PLIST_ENTRY ListHead
)
42 PLIST_ENTRY current_entry
;
45 DbgPrint("MmDumpMemoryAreas()\n");
47 current_entry
= ListHead
->Flink
;
48 while (current_entry
!=ListHead
)
50 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
51 DbgPrint("Base %x Length %x End %x Attributes %x Flink %x\n",
52 current
->BaseAddress
,current
->Length
,
53 (char*)current
->BaseAddress
+current
->Length
,current
->Attributes
,
54 current
->Entry
.Flink
);
55 current_entry
= current_entry
->Flink
;
57 DbgPrint("Finished MmDumpMemoryAreas()\n");
60 MEMORY_AREA
* MmOpenMemoryAreaByAddress(PMADDRESS_SPACE AddressSpace
,
63 PLIST_ENTRY current_entry
;
65 PLIST_ENTRY previous_entry
;
67 DPRINT("MmOpenMemoryAreaByAddress(AddressSpace %x, Address %x)\n",
68 AddressSpace
, Address
);
70 previous_entry
= &AddressSpace
->MAreaListHead
;
71 current_entry
= AddressSpace
->MAreaListHead
.Flink
;
72 while (current_entry
!= &AddressSpace
->MAreaListHead
)
74 current
= CONTAINING_RECORD(current_entry
,
77 assert(current_entry
->Blink
->Flink
== current_entry
);
78 assert(current_entry
->Flink
->Blink
== current_entry
);
79 assert(previous_entry
->Flink
== current_entry
);
80 if (current
->BaseAddress
<= Address
&&
81 (PVOID
)((char*)current
->BaseAddress
+ current
->Length
) > Address
)
83 DPRINT("%s() = %x\n",__FUNCTION__
,current
);
86 if (current
->BaseAddress
> Address
)
88 DPRINT("%s() = NULL\n",__FUNCTION__
);
91 previous_entry
= current_entry
;
92 current_entry
= current_entry
->Flink
;
94 DPRINT("%s() = NULL\n",__FUNCTION__
);
98 MEMORY_AREA
* MmOpenMemoryAreaByRegion(PMADDRESS_SPACE AddressSpace
,
102 PLIST_ENTRY current_entry
;
103 MEMORY_AREA
* current
;
106 DPRINT("MmOpenMemoryByRegion(AddressSpace %x, Address %x, Length %x)\n",
107 AddressSpace
, Address
, Length
);
109 current_entry
= AddressSpace
->MAreaListHead
.Flink
;
110 while (current_entry
!= &AddressSpace
->MAreaListHead
)
112 current
= CONTAINING_RECORD(current_entry
,
115 DPRINT("current->BaseAddress %x current->Length %x\n",
116 current
->BaseAddress
,current
->Length
);
117 if (current
->BaseAddress
>= Address
&&
118 current
->BaseAddress
< (PVOID
)((char*)Address
+Length
))
120 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
124 Extent
= (ULONG
)current
->BaseAddress
+ current
->Length
;
125 if (Extent
> (ULONG
)Address
&&
126 Extent
< (ULONG
)((char*)Address
+Length
))
128 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
132 if (current
->BaseAddress
<= Address
&&
133 Extent
>= (ULONG
)((char*)Address
+Length
))
135 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
139 if (current
->BaseAddress
>= (PVOID
)((char*)Address
+Length
))
141 DPRINT("Finished MmOpenMemoryAreaByRegion()= NULL\n",0);
144 current_entry
= current_entry
->Flink
;
146 DPRINT("Finished MmOpenMemoryAreaByRegion() = NULL\n",0);
150 static VOID
MmInsertMemoryArea(PMADDRESS_SPACE AddressSpace
,
153 PLIST_ENTRY ListHead
;
154 PLIST_ENTRY current_entry
;
155 PLIST_ENTRY inserted_entry
= &marea
->Entry
;
156 MEMORY_AREA
* current
;
159 DPRINT("MmInsertMemoryArea(marea %x)\n", marea
);
160 DPRINT("marea->BaseAddress %x\n", marea
->BaseAddress
);
161 DPRINT("marea->Length %x\n", marea
->Length
);
163 ListHead
= &AddressSpace
->MAreaListHead
;
165 current_entry
= ListHead
->Flink
;
166 if (IsListEmpty(ListHead
))
168 InsertHeadList(ListHead
,&marea
->Entry
);
171 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
172 if (current
->BaseAddress
> marea
->BaseAddress
)
174 InsertHeadList(ListHead
,&marea
->Entry
);
177 while (current_entry
->Flink
!=ListHead
)
179 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
180 next
= CONTAINING_RECORD(current_entry
->Flink
,MEMORY_AREA
,Entry
);
181 if (current
->BaseAddress
< marea
->BaseAddress
&&
182 current
->Entry
.Flink
==ListHead
)
184 current_entry
->Flink
= inserted_entry
;
185 inserted_entry
->Flink
=ListHead
;
186 inserted_entry
->Blink
=current_entry
;
187 ListHead
->Blink
= inserted_entry
;
190 if (current
->BaseAddress
< marea
->BaseAddress
&&
191 next
->BaseAddress
> marea
->BaseAddress
)
193 inserted_entry
->Flink
= current_entry
->Flink
;
194 inserted_entry
->Blink
= current_entry
;
195 inserted_entry
->Flink
->Blink
= inserted_entry
;
196 current_entry
->Flink
=inserted_entry
;
199 current_entry
= current_entry
->Flink
;
201 InsertTailList(ListHead
,inserted_entry
);
204 static PVOID
MmFindGapBottomUp(PMADDRESS_SPACE AddressSpace
, ULONG Length
, ULONG Granularity
)
206 PLIST_ENTRY ListHead
;
207 PLIST_ENTRY current_entry
;
208 MEMORY_AREA
* current
;
213 DPRINT("MmFindGapBottomUp(Length %x)\n",Length
);
216 Length
+= PAGE_SIZE
; /* For a guard page following the area */
219 ListHead
= &AddressSpace
->MAreaListHead
;
221 current_entry
= ListHead
->Flink
;
222 while (current_entry
->Flink
!=ListHead
)
224 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
225 next
= CONTAINING_RECORD(current_entry
->Flink
,MEMORY_AREA
,Entry
);
226 Address
= (PVOID
) ((char*)current
->BaseAddress
+ PAGE_ROUND_UP(current
->Length
));
228 Address
= (PVOID
) ((char *) Address
+ PAGE_SIZE
); /* For a guard page preceding the area */
230 Address
= (PVOID
) MM_ROUND_UP(Address
, Granularity
);
231 if (Address
< next
->BaseAddress
)
233 Gap
= (char*)next
->BaseAddress
- ((char*)current
->BaseAddress
+ PAGE_ROUND_UP(current
->Length
));
239 current_entry
= current_entry
->Flink
;
242 if (current_entry
== ListHead
)
244 Address
= (PVOID
) MM_ROUND_UP(AddressSpace
->LowestAddress
, Granularity
);
248 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
249 Address
= (char*)current
->BaseAddress
+ PAGE_ROUND_UP(current
->Length
);
251 Address
= (PVOID
) ((char *) Address
+ PAGE_SIZE
); /* For a guard page preceding the area */
253 Address
= (PVOID
) MM_ROUND_UP(Address
, Granularity
);
255 /* Check if enough space for the block */
256 if (AddressSpace
->LowestAddress
< KERNEL_BASE
)
258 if ((ULONG_PTR
) Address
>= KERNEL_BASE
|| Length
> KERNEL_BASE
- (ULONG_PTR
) Address
)
260 DPRINT1("Failed to find gap\n");
266 if (Length
>= ~ ((ULONG_PTR
) 0) - (ULONG_PTR
) Address
)
268 DPRINT1("Failed to find gap\n");
276 static PVOID
MmFindGapTopDown(PMADDRESS_SPACE AddressSpace
, ULONG Length
, ULONG Granularity
)
278 PLIST_ENTRY ListHead
;
279 PLIST_ENTRY current_entry
;
280 MEMORY_AREA
* current
;
285 PVOID HighestAddress
;
287 DPRINT("MmFindGapTopDown(Length %lx)\n",Length
);
290 Length
+= PAGE_SIZE
; /* For a guard page following the area */
293 if (AddressSpace
->LowestAddress
< KERNEL_BASE
) //(ULONG_PTR)MmSystemRangeStart)
295 HighestAddress
= MmHighestUserAddress
;
299 HighestAddress
= (PVOID
)0xFFFFFFFF;
302 TopAddress
= HighestAddress
;
304 ListHead
= &AddressSpace
->MAreaListHead
;
305 current_entry
= ListHead
->Blink
;
306 while (current_entry
->Blink
!= ListHead
)
308 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
309 BottomAddress
= (char*)current
->BaseAddress
+ PAGE_ROUND_UP(current
->Length
);
311 BottomAddress
= (PVOID
) ((char *) BottomAddress
+ PAGE_SIZE
); /* For a guard page preceding the area */
313 BottomAddress
= (PVOID
) MM_ROUND_UP(BottomAddress
, Granularity
);
314 DPRINT("Base %p Length %lx\n", current
->BaseAddress
, PAGE_ROUND_UP(current
->Length
));
316 if (BottomAddress
< TopAddress
&& BottomAddress
< HighestAddress
)
318 Gap
= (char*)TopAddress
- (char*) BottomAddress
+ 1;
319 DPRINT("Bottom %p Top %p Gap %lx\n", BottomAddress
, TopAddress
, Gap
);
322 DPRINT("Found gap at %p\n", (char*) TopAddress
- Length
);
323 return (PVOID
) MM_ROUND_DOWN((char*) TopAddress
- Length
+ 1, Granularity
);
326 TopAddress
= (char*)current
->BaseAddress
- 1;
327 current_entry
= current_entry
->Blink
;
330 if (current_entry
== ListHead
)
332 Address
= (PVOID
) MM_ROUND_DOWN((char*) HighestAddress
- Length
+ 1, Granularity
);
336 Address
= (PVOID
) MM_ROUND_DOWN((char*)TopAddress
- Length
+ 1, Granularity
);
339 /* Check if enough space for the block */
340 if (AddressSpace
->LowestAddress
< KERNEL_BASE
)
342 if ((ULONG_PTR
) Address
>= KERNEL_BASE
|| Length
> KERNEL_BASE
- (ULONG_PTR
) Address
)
344 DPRINT1("Failed to find gap\n");
350 if (Length
>= ~ ((ULONG_PTR
) 0) - (ULONG_PTR
) Address
)
352 DPRINT1("Failed to find gap\n");
357 DPRINT("Found gap at %p\n", Address
);
362 PVOID
MmFindGap(PMADDRESS_SPACE AddressSpace
, ULONG Length
, ULONG Granularity
, BOOL TopDown
)
365 return MmFindGapTopDown(AddressSpace
, Length
, Granularity
);
367 return MmFindGapBottomUp(AddressSpace
, Length
, Granularity
);
370 ULONG
MmFindGapAtAddress(PMADDRESS_SPACE AddressSpace
, PVOID Address
)
372 PLIST_ENTRY current_entry
, ListHead
;
373 PMEMORY_AREA current
;
375 Address
= (PVOID
)PAGE_ROUND_DOWN(Address
);
377 if (AddressSpace
->LowestAddress
< KERNEL_BASE
)
379 if (Address
>= (PVOID
)KERNEL_BASE
)
386 if ((ULONG_PTR
)Address
< AddressSpace
->LowestAddress
)
392 ListHead
= &AddressSpace
->MAreaListHead
;
394 current_entry
= ListHead
->Flink
;
395 while (current_entry
!= ListHead
)
397 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
398 if (current
->BaseAddress
<= Address
&& (char*)Address
< (char*)current
->BaseAddress
+ current
->Length
)
402 else if (current
->BaseAddress
> Address
)
404 return (ULONG_PTR
)current
->BaseAddress
- (ULONG_PTR
)Address
;
406 current_entry
= current_entry
->Flink
;
408 if (AddressSpace
->LowestAddress
< KERNEL_BASE
)
410 return KERNEL_BASE
- (ULONG_PTR
)Address
;
414 return 0 - (ULONG_PTR
)Address
;
418 NTSTATUS INIT_FUNCTION
419 MmInitMemoryAreas(VOID
)
421 * FUNCTION: Initialize the memory area list
424 DPRINT("MmInitMemoryAreas()\n",0);
425 return(STATUS_SUCCESS
);
429 MmFreeMemoryArea(PMADDRESS_SPACE AddressSpace
,
432 VOID (*FreePage
)(PVOID Context
, MEMORY_AREA
* MemoryArea
,
433 PVOID Address
, PFN_TYPE Page
,
434 SWAPENTRY SwapEntry
, BOOLEAN Dirty
),
435 PVOID FreePageContext
)
437 MEMORY_AREA
* MemoryArea
;
440 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
442 DPRINT("MmFreeMemoryArea(AddressSpace %x, BaseAddress %x, Length %x,"
443 "FreePageContext %d)\n",AddressSpace
,BaseAddress
,Length
,
446 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
448 if (MemoryArea
== NULL
)
451 return(STATUS_UNSUCCESSFUL
);
453 if (AddressSpace
->Process
!= NULL
&&
454 AddressSpace
->Process
!= CurrentProcess
)
456 KeAttachProcess(AddressSpace
->Process
);
458 EndAddress
= (char*)MemoryArea
->BaseAddress
+ PAGE_ROUND_UP(MemoryArea
->Length
);
459 for (Address
= MemoryArea
->BaseAddress
; Address
< EndAddress
; Address
+= PAGE_SIZE
)
462 if (MemoryArea
->Type
== MEMORY_AREA_IO_MAPPING
)
464 MmRawDeleteVirtualMapping(Address
);
469 SWAPENTRY SwapEntry
= 0;
473 if (MmIsPageSwapEntry(AddressSpace
->Process
, Address
))
475 MmDeletePageFileMapping(AddressSpace
->Process
, Address
, &SwapEntry
);
479 MmDeleteVirtualMapping(AddressSpace
->Process
, Address
, FALSE
, &Dirty
, &Page
);
481 if (FreePage
!= NULL
)
483 FreePage(FreePageContext
, MemoryArea
, Address
,
484 Page
, SwapEntry
, (BOOLEAN
)Dirty
);
488 if (AddressSpace
->Process
!= NULL
&&
489 AddressSpace
->Process
!= CurrentProcess
)
493 RemoveEntryList(&MemoryArea
->Entry
);
494 ExFreePool(MemoryArea
);
496 DPRINT("MmFreeMemoryArea() succeeded\n");
498 return(STATUS_SUCCESS
);
501 PMEMORY_AREA
MmSplitMemoryArea(PEPROCESS Process
,
502 PMADDRESS_SPACE AddressSpace
,
503 PMEMORY_AREA OriginalMemoryArea
,
512 Result
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MEMORY_AREA
),
514 RtlZeroMemory(Result
,sizeof(MEMORY_AREA
));
515 Result
->Type
= NewType
;
516 Result
->BaseAddress
= BaseAddress
;
517 Result
->Length
= Length
;
518 Result
->Attributes
= NewAttributes
;
519 Result
->LockCount
= 0;
520 Result
->Process
= Process
;
522 if (BaseAddress
== OriginalMemoryArea
->BaseAddress
)
524 OriginalMemoryArea
->BaseAddress
= (char*)BaseAddress
+ Length
;
525 OriginalMemoryArea
->Length
= OriginalMemoryArea
->Length
- Length
;
526 MmInsertMemoryArea(AddressSpace
, Result
);
529 if (((char*)BaseAddress
+ Length
) ==
530 ((char*)OriginalMemoryArea
->BaseAddress
+ OriginalMemoryArea
->Length
))
532 OriginalMemoryArea
->Length
= OriginalMemoryArea
->Length
- Length
;
533 MmInsertMemoryArea(AddressSpace
, Result
);
538 Split
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MEMORY_AREA
),
540 RtlCopyMemory(Split
,OriginalMemoryArea
,sizeof(MEMORY_AREA
));
541 Split
->BaseAddress
= (char*)BaseAddress
+ Length
;
542 Split
->Length
= OriginalMemoryArea
->Length
- (((ULONG
)BaseAddress
)
545 OriginalMemoryArea
->Length
= (char*)BaseAddress
- (char*)OriginalMemoryArea
->BaseAddress
;
550 NTSTATUS
MmCreateMemoryArea(PEPROCESS Process
,
551 PMADDRESS_SPACE AddressSpace
,
556 MEMORY_AREA
** Result
,
559 PHYSICAL_ADDRESS BoundaryAddressMultiple
)
561 * FUNCTION: Create a memory area
563 * AddressSpace = Address space to create the area in
564 * Type = Type of the address space
566 * Length = Length to allocate
567 * Attributes = Protection attributes for the memory area
568 * Result = Receives a pointer to the memory area on exit
570 * NOTES: Lock the address space before calling this function
576 DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %x,"
577 "*BaseAddress %x, Length %x, Attributes %x, Result %x)\n",
578 Type
,BaseAddress
,*BaseAddress
,Length
,Attributes
,Result
);
580 Granularity
= (MEMORY_AREA_VIRTUAL_MEMORY
== Type
? MM_VIRTMEM_GRANULARITY
: PAGE_SIZE
);
581 if ((*BaseAddress
) == 0 && !FixedAddress
)
583 tmpLength
= PAGE_ROUND_UP(Length
);
584 *BaseAddress
= MmFindGap(AddressSpace
,
585 PAGE_ROUND_UP(Length
),
588 if ((*BaseAddress
) == 0)
590 DPRINT("No suitable gap\n");
591 return STATUS_NO_MEMORY
;
596 tmpLength
= Length
+ ((ULONG_PTR
) *BaseAddress
597 - (ULONG_PTR
) MM_ROUND_DOWN(*BaseAddress
, Granularity
));
598 *BaseAddress
= MM_ROUND_DOWN(*BaseAddress
, Granularity
);
600 if (AddressSpace
->LowestAddress
== KERNEL_BASE
&&
601 (*BaseAddress
) < (PVOID
)KERNEL_BASE
)
603 return STATUS_ACCESS_VIOLATION
;
606 if (AddressSpace
->LowestAddress
< KERNEL_BASE
&&
607 (PVOID
)((char*)(*BaseAddress
) + tmpLength
) > (PVOID
)KERNEL_BASE
)
609 return STATUS_ACCESS_VIOLATION
;
612 if (BoundaryAddressMultiple
.QuadPart
!= 0)
614 EndAddress
= ((char*)(*BaseAddress
)) + tmpLength
-1;
615 assert(((DWORD_PTR
)*BaseAddress
/BoundaryAddressMultiple
.QuadPart
) == ((DWORD_PTR
)EndAddress
/BoundaryAddressMultiple
.QuadPart
));
618 if (MmOpenMemoryAreaByRegion(AddressSpace
,
622 DPRINT("Memory area already occupied\n");
623 return STATUS_CONFLICTING_ADDRESSES
;
627 *Result
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MEMORY_AREA
),
629 RtlZeroMemory(*Result
,sizeof(MEMORY_AREA
));
630 (*Result
)->Type
= Type
;
631 (*Result
)->BaseAddress
= *BaseAddress
;
632 (*Result
)->Length
= tmpLength
;
633 (*Result
)->Attributes
= Attributes
;
634 (*Result
)->LockCount
= 0;
635 (*Result
)->Process
= Process
;
636 (*Result
)->PageOpCount
= 0;
637 (*Result
)->DeleteInProgress
= FALSE
;
639 MmInsertMemoryArea(AddressSpace
, *Result
);
641 DPRINT("MmCreateMemoryArea() succeeded\n");
642 return STATUS_SUCCESS
;
647 MmReleaseMemoryAreaIfDecommitted(PEPROCESS Process
,
648 PMADDRESS_SPACE AddressSpace
,
651 PMEMORY_AREA MemoryArea
;
656 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
, BaseAddress
);
657 if (NULL
!= MemoryArea
)
659 Entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
661 while (Reserved
&& Entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
663 Region
= CONTAINING_RECORD(Entry
, MM_REGION
, RegionListEntry
);
664 Reserved
= (MEM_RESERVE
== Region
->Type
);
665 Entry
= Entry
->Flink
;
670 DPRINT("Release TebBlock at %p\n", TebBlock
);
671 MmFreeVirtualMemory(Process
, MemoryArea
);