3 * Copyright (C) 1998-2002 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 *****************************************************************/
31 #include <ddk/ntddk.h>
32 #include <internal/mm.h>
33 #include <internal/ps.h>
34 #include <internal/pool.h>
37 #include <internal/debug.h>
39 /* GLOBALS *******************************************************************/
41 #define TAG_MAREA TAG('M', 'A', 'R', 'E')
44 PVOID MiMemoryAreaBugCheckAddress
= (PVOID
) NULL
;
47 /* Define to track memory area references */
48 //#define TRACK_MEMORY_AREA_REFERENCES
50 /* FUNCTIONS *****************************************************************/
52 VOID
MmDumpMemoryAreas(PLIST_ENTRY ListHead
)
54 PLIST_ENTRY current_entry
;
57 DbgPrint("MmDumpMemoryAreas()\n");
59 current_entry
= ListHead
->Flink
;
60 while (current_entry
!=ListHead
)
62 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
63 DbgPrint("Base %x Length %x End %x Attributes %x Flink %x\n",
64 current
->BaseAddress
,current
->Length
,
65 current
->BaseAddress
+current
->Length
,current
->Attributes
,
66 current
->Entry
.Flink
);
67 current_entry
= current_entry
->Flink
;
69 DbgPrint("Finished MmDumpMemoryAreas()\n");
75 MiValidateMemoryAreaPTEs(IN PMEMORY_AREA MemoryArea
)
83 for (i
= 0; i
<= (MemoryArea
->Length
/ PAGESIZE
); i
++)
85 if (MmIsPagePresent(MemoryArea
->Process
, MemoryArea
->BaseAddress
+ (i
* PAGESIZE
)))
87 PteProtect
= MmGetPageProtect(MemoryArea
->Process
, MemoryArea
->BaseAddress
+ (i
* PAGESIZE
));
88 if (PteProtect
!= MemoryArea
->Attributes
)
90 if (MmIsCopyOnWriteMemoryArea(MemoryArea
))
92 if ((PteProtect
!= PAGE_READONLY
) && (PteProtect
!= PAGE_EXECUTE_READ
))
94 DPRINT1("COW memory area attributes 0x%.08x\n", MemoryArea
->Attributes
);
95 DbgMmDumpProtection(MemoryArea
->Attributes
);
96 DPRINT1("PTE attributes 0x%.08x\n", PteProtect
);
97 DbgMmDumpProtection(PteProtect
);
98 assertmsg(FALSE
, ("PTE attributes and memory area protection are different. Area 0x%.08x\n",
99 MemoryArea
->BaseAddress
));
104 DPRINT1("Memory area attributes 0x%.08x\n", MemoryArea
->Attributes
);
105 DbgMmDumpProtection(MemoryArea
->Attributes
);
106 DPRINT1("PTE attributes 0x%.08x\n", PteProtect
);
107 DbgMmDumpProtection(PteProtect
);
108 assertmsg(FALSE
, ("PTE attributes and memory area protection are different. Area 0x%.08x\n",
109 MemoryArea
->BaseAddress
));
118 MiValidateMemoryArea(IN PMEMORY_AREA MemoryArea
)
120 assertmsg(MemoryArea
!= NULL
,
121 ("No memory area can exist at 0x%.08x\n", MemoryArea
));
123 assertmsg(MemoryArea
->Magic
== TAG_MAREA
,
124 ("Bad magic (0x%.08x) for memory area (0x%.08x). It should be 0x%.08x\n",
125 MemoryArea
->Magic
, MemoryArea
, TAG_MAREA
));
127 /* FIXME: Can cause page faults and deadlock on the address space lock */
128 //MiValidateMemoryAreaPTEs(MemoryArea);
134 MmApplyMemoryAreaProtection(IN PMEMORY_AREA MemoryArea
)
141 for (i
= 0; i
<= (MemoryArea
->Length
/ PAGESIZE
); i
++)
143 if (MmIsPagePresent(MemoryArea
->Process
, MemoryArea
->BaseAddress
+ (i
* PAGESIZE
)))
145 MmSetPageProtect(MemoryArea
->Process
,
146 MemoryArea
->BaseAddress
+ (i
* PAGESIZE
),
147 MemoryArea
->Attributes
);
154 * NOTE: If the memory area is found, then it is referenced. The caller must
155 * call MmCloseMemoryArea() after use.
158 MmOpenMemoryAreaByAddress(IN PMADDRESS_SPACE AddressSpace
,
161 PLIST_ENTRY current_entry
;
162 MEMORY_AREA
* current
;
163 PLIST_ENTRY previous_entry
;
165 DPRINT("MmOpenMemoryAreaByAddress(AddressSpace %x, Address %x)\n",
166 AddressSpace
, Address
);
168 // MmDumpMemoryAreas(&AddressSpace->MAreaListHead);
170 previous_entry
= &AddressSpace
->MAreaListHead
;
171 current_entry
= AddressSpace
->MAreaListHead
.Flink
;
172 while (current_entry
!= &AddressSpace
->MAreaListHead
)
174 current
= CONTAINING_RECORD(current_entry
,
177 DPRINT("Scanning %x BaseAddress %x Length %x\n",
178 current
, current
->BaseAddress
, current
->Length
);
179 assert(current_entry
->Blink
->Flink
== current_entry
);
180 if (current_entry
->Flink
->Blink
!= current_entry
)
182 DPRINT1("BaseAddress %x\n", current
->BaseAddress
);
183 DPRINT1("current_entry->Flink %x ", current_entry
->Flink
);
184 DPRINT1("¤t_entry->Flink %x\n",
185 ¤t_entry
->Flink
);
186 DPRINT1("current_entry->Flink->Blink %x\n",
187 current_entry
->Flink
->Blink
);
188 DPRINT1("¤t_entry->Flink->Blink %x\n",
189 ¤t_entry
->Flink
->Blink
);
190 DPRINT1("¤t_entry->Flink %x\n",
191 ¤t_entry
->Flink
);
193 assert(current_entry
->Flink
->Blink
== current_entry
);
194 assert(previous_entry
->Flink
== current_entry
);
195 if (current
->BaseAddress
<= Address
&&
196 (current
->BaseAddress
+ current
->Length
) > Address
)
198 DPRINT("%s() = %x\n",__FUNCTION__
,current
);
199 MmReferenceMemoryArea(current
);
202 if (current
->BaseAddress
> Address
)
204 DPRINT("%s() = NULL\n",__FUNCTION__
);
207 previous_entry
= current_entry
;
208 current_entry
= current_entry
->Flink
;
210 DPRINT("%s() = NULL\n",__FUNCTION__
);
215 * NOTE: If the memory area is found, then it is referenced. The caller must
216 * call MmCloseMemoryArea() after use.
218 MEMORY_AREA
* MmOpenMemoryAreaByRegion(PMADDRESS_SPACE AddressSpace
,
222 PLIST_ENTRY current_entry
;
223 MEMORY_AREA
* current
;
226 DPRINT("MmOpenMemoryByRegion(AddressSpace %x, Address %x, Length %x)\n",
227 AddressSpace
, Address
, Length
);
229 current_entry
= AddressSpace
->MAreaListHead
.Flink
;
230 while (current_entry
!= &AddressSpace
->MAreaListHead
)
232 current
= CONTAINING_RECORD(current_entry
,
235 DPRINT("current->BaseAddress %x current->Length %x\n",
236 current
->BaseAddress
,current
->Length
);
237 if (current
->BaseAddress
>= Address
&&
238 current
->BaseAddress
< (Address
+Length
))
240 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
242 MmReferenceMemoryArea(current
);
245 Extent
= (ULONG
)current
->BaseAddress
+ current
->Length
;
246 if (Extent
> (ULONG
)Address
&&
247 Extent
< (ULONG
)(Address
+Length
))
249 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
251 MmReferenceMemoryArea(current
);
254 if (current
->BaseAddress
<= Address
&&
255 Extent
>= (ULONG
)(Address
+Length
))
257 DPRINT("Finished MmOpenMemoryAreaByRegion() = %x\n",
259 MmReferenceMemoryArea(current
);
262 if (current
->BaseAddress
>= (Address
+Length
))
264 DPRINT("Finished MmOpenMemoryAreaByRegion()= NULL\n",0);
267 current_entry
= current_entry
->Flink
;
269 DPRINT("Finished MmOpenMemoryAreaByRegion() = NULL\n",0);
275 MmCloseMemoryArea(IN PMEMORY_AREA MemoryArea
)
277 MmDereferenceMemoryArea(MemoryArea
);
281 static VOID
MmInsertMemoryArea(PMADDRESS_SPACE AddressSpace
,
284 PLIST_ENTRY ListHead
;
285 PLIST_ENTRY current_entry
;
286 PLIST_ENTRY inserted_entry
= &marea
->Entry
;
287 MEMORY_AREA
* current
;
290 DPRINT("MmInsertMemoryArea(marea %x)\n", marea
);
291 DPRINT("marea->BaseAddress %x\n", marea
->BaseAddress
);
292 DPRINT("marea->Length %x\n", marea
->Length
);
294 ListHead
= &AddressSpace
->MAreaListHead
;
296 current_entry
= ListHead
->Flink
;
298 if (IsListEmpty(ListHead
))
301 InsertHeadList(ListHead
,&marea
->Entry
);
302 DPRINT("Inserting at list head\n");
307 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
309 if (current
->BaseAddress
> marea
->BaseAddress
)
312 InsertHeadList(ListHead
,&marea
->Entry
);
313 DPRINT("Inserting at list head\n");
318 while (current_entry
->Flink
!=ListHead
)
321 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
322 next
= CONTAINING_RECORD(current_entry
->Flink
,MEMORY_AREA
,Entry
);
323 // assert(current->BaseAddress != marea->BaseAddress);
324 // assert(next->BaseAddress != marea->BaseAddress);
325 if (current
->BaseAddress
< marea
->BaseAddress
&&
326 current
->Entry
.Flink
==ListHead
)
328 DPRINT("Insert after %x\n", current_entry
);
329 current_entry
->Flink
= inserted_entry
;
330 inserted_entry
->Flink
=ListHead
;
331 inserted_entry
->Blink
=current_entry
;
332 ListHead
->Blink
= inserted_entry
;
335 if (current
->BaseAddress
< marea
->BaseAddress
&&
336 next
->BaseAddress
> marea
->BaseAddress
)
338 DPRINT("Inserting before %x\n", current_entry
);
339 inserted_entry
->Flink
= current_entry
->Flink
;
340 inserted_entry
->Blink
= current_entry
;
341 inserted_entry
->Flink
->Blink
= inserted_entry
;
342 current_entry
->Flink
=inserted_entry
;
345 current_entry
= current_entry
->Flink
;
348 DPRINT("Inserting at list tail\n");
349 InsertTailList(ListHead
,inserted_entry
);
352 static PVOID
MmFindGap(PMADDRESS_SPACE AddressSpace
,
355 PLIST_ENTRY ListHead
;
356 PLIST_ENTRY current_entry
;
357 MEMORY_AREA
* current
;
361 DPRINT("MmFindGap(Length %x)\n",Length
);
363 ListHead
= &AddressSpace
->MAreaListHead
;
365 current_entry
= ListHead
->Flink
;
366 while (current_entry
->Flink
!=ListHead
)
368 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
369 next
= CONTAINING_RECORD(current_entry
->Flink
,MEMORY_AREA
,Entry
);
370 DPRINT("current %x current->BaseAddress %x ",current
,
371 current
->BaseAddress
);
372 DPRINT("current->Length %x\n",current
->Length
);
373 DPRINT("next %x next->BaseAddress %x ",next
,next
->BaseAddress
);
374 Gap
= (next
->BaseAddress
) -(current
->BaseAddress
+ current
->Length
);
375 DPRINT("Base %x Gap %x\n",current
->BaseAddress
,Gap
);
378 return(current
->BaseAddress
+ PAGE_ROUND_UP(current
->Length
));
380 current_entry
= current_entry
->Flink
;
383 if (current_entry
== ListHead
)
385 return((PVOID
)AddressSpace
->LowestAddress
);
388 current
= CONTAINING_RECORD(current_entry
,MEMORY_AREA
,Entry
);
389 //DbgPrint("current %x returning %x\n",current,current->BaseAddress+
391 return(current
->BaseAddress
+ PAGE_ROUND_UP(current
->Length
));
394 NTSTATUS
MmInitMemoryAreas(VOID
)
396 * FUNCTION: Initialize the memory area list
399 DPRINT("MmInitMemoryAreas()\n",0);
400 return(STATUS_SUCCESS
);
403 /* NOTE: The address space lock must be held when called */
405 MmFreeMemoryArea(IN PMADDRESS_SPACE AddressSpace
,
406 IN PVOID BaseAddress
,
408 IN PFREE_MEMORY_AREA_PAGE_CALLBACK FreePage
,
409 IN PVOID FreePageContext
)
411 MEMORY_AREA
* MemoryArea
;
414 DPRINT("MmFreeMemoryArea(AddressSpace %x, BaseAddress %x, Length %x, "
415 "FreePageContext %d)\n",AddressSpace
,BaseAddress
,Length
,FreePageContext
);
417 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
, BaseAddress
);
418 if (MemoryArea
== NULL
)
420 assertmsg(FALSE
, ("Freeing non-existant memory area at 0x%.08x\n", BaseAddress
));
421 return(STATUS_UNSUCCESSFUL
);
424 MmCloseMemoryArea(MemoryArea
);
425 InterlockedDecrement(&MemoryArea
->ReferenceCount
);
427 assertmsg(MemoryArea
->ReferenceCount
== 0,
428 ("Memory area at address 0x%.08x has %d outstanding references\n",
429 BaseAddress
, MemoryArea
->ReferenceCount
));
431 for (i
=0; i
<(PAGE_ROUND_UP(MemoryArea
->Length
)/PAGESIZE
); i
++)
433 ULONG_PTR PhysicalPage
= 0;
434 BOOLEAN Dirty
= FALSE
;
435 SWAPENTRY SwapEntry
= 0;
436 PVOID VirtualPage
= NULL
;
438 VirtualPage
= MemoryArea
->BaseAddress
+ (i
* PAGESIZE
);
441 if ((MiMemoryAreaBugCheckAddress
!= NULL
)
442 && ((MiMemoryAreaBugCheckAddress
>= VirtualPage
)
443 && MiMemoryAreaBugCheckAddress
< VirtualPage
+ PAGESIZE
))
445 assertmsg(FALSE
, ("VirtualPage 0x%.08x MiMemoryAreaBugCheckAddress 0x%.08x \n",
450 if (FreePage
!= NULL
)
452 FreePage(TRUE
, FreePageContext
, MemoryArea
,
453 VirtualPage
, 0, 0, FALSE
);
456 if (MmIsPageSwapEntry(AddressSpace
->Process
, VirtualPage
))
458 MmDeletePageFileMapping(AddressSpace
->Process
,
464 MmDeleteVirtualMapping(AddressSpace
->Process
,
466 FALSE
, &Dirty
, &PhysicalPage
);
468 if (FreePage
!= NULL
)
470 FreePage(FALSE
, FreePageContext
, MemoryArea
,
471 VirtualPage
, PhysicalPage
, SwapEntry
, Dirty
);
475 RemoveEntryList(&MemoryArea
->Entry
);
476 ExFreePool(MemoryArea
);
478 DPRINT("MmFreeMemoryArea() succeeded\n");
480 return(STATUS_SUCCESS
);
483 PMEMORY_AREA
MmSplitMemoryArea(PEPROCESS Process
,
484 PMADDRESS_SPACE AddressSpace
,
485 PMEMORY_AREA OriginalMemoryArea
,
494 Result
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MEMORY_AREA
),
496 RtlZeroMemory(Result
,sizeof(MEMORY_AREA
));
497 Result
->Type
= NewType
;
498 Result
->BaseAddress
= BaseAddress
;
499 Result
->Length
= Length
;
500 Result
->Attributes
= NewAttributes
;
501 Result
->LockCount
= 0;
502 Result
->ReferenceCount
= 1;
503 Result
->Process
= Process
;
505 if (BaseAddress
== OriginalMemoryArea
->BaseAddress
)
507 OriginalMemoryArea
->BaseAddress
= BaseAddress
+ Length
;
508 OriginalMemoryArea
->Length
= OriginalMemoryArea
->Length
- Length
;
509 MmInsertMemoryArea(AddressSpace
, Result
);
512 if ((BaseAddress
+ Length
) ==
513 (OriginalMemoryArea
->BaseAddress
+ OriginalMemoryArea
->Length
))
515 OriginalMemoryArea
->Length
= OriginalMemoryArea
->Length
- Length
;
516 MmInsertMemoryArea(AddressSpace
, Result
);
521 Split
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MEMORY_AREA
),
523 RtlCopyMemory(Split
,OriginalMemoryArea
,sizeof(MEMORY_AREA
));
524 Split
->BaseAddress
= BaseAddress
+ Length
;
525 Split
->Length
= OriginalMemoryArea
->Length
- (((ULONG
)BaseAddress
)
528 OriginalMemoryArea
->Length
= BaseAddress
- OriginalMemoryArea
->BaseAddress
;
534 MmCreateMemoryArea(IN PEPROCESS Process
,
535 IN PMADDRESS_SPACE AddressSpace
,
537 IN OUT PVOID
* BaseAddress
,
540 OUT PMEMORY_AREA
* Result
,
541 IN BOOLEAN FixedAddress
)
543 * FUNCTION: Create a memory area
545 * AddressSpace = Address space to create the area in
546 * Type = Type of the address space
548 * Length = Length to allocate
549 * Attributes = Protection attributes for the memory area
550 * Result = Receives a pointer to the memory area on exit
551 * FixedAddress = Wether the memory area must be based at BaseAddress or not
553 * NOTES: Lock the address space before calling this function
556 PMEMORY_AREA MemoryArea
;
558 DPRINT("MmCreateMemoryArea(Type %d, BaseAddress %x,"
559 "*BaseAddress %x, Length %x, Attributes %x, Result %x)\n",
560 Type
,BaseAddress
,*BaseAddress
,Length
,Attributes
,Result
);
562 if ((*BaseAddress
)==0 && !FixedAddress
)
564 *BaseAddress
= MmFindGap(AddressSpace
,
565 PAGE_ROUND_UP(Length
) +(PAGESIZE
*2));
566 if ((*BaseAddress
)==0)
568 DPRINT("No suitable gap\n");
569 return(STATUS_NO_MEMORY
);
571 (*BaseAddress
)=(*BaseAddress
)+PAGESIZE
;
575 (*BaseAddress
) = (PVOID
)PAGE_ROUND_DOWN((*BaseAddress
));
576 MemoryArea
= MmOpenMemoryAreaByRegion(AddressSpace
, *BaseAddress
, Length
);
579 MmCloseMemoryArea(MemoryArea
);
580 DPRINT("Memory area already occupied\n");
581 return(STATUS_CONFLICTING_ADDRESSES
);
585 DPRINT("MmCreateMemoryArea(*BaseAddress %x)\n", *BaseAddress
);
587 *Result
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MEMORY_AREA
),
589 RtlZeroMemory(*Result
,sizeof(MEMORY_AREA
));
590 SET_MAGIC(*Result
, TAG_MAREA
)
591 (*Result
)->Type
= Type
;
592 (*Result
)->BaseAddress
= *BaseAddress
;
593 (*Result
)->Length
= Length
;
594 (*Result
)->Attributes
= Attributes
;
595 (*Result
)->LockCount
= 0;
596 (*Result
)->ReferenceCount
= 1;
597 (*Result
)->Process
= Process
;
599 MmApplyMemoryAreaProtection(*Result
);
601 MmInsertMemoryArea(AddressSpace
, *Result
);
603 DPRINT("MmCreateMemoryArea() succeeded\n");
604 return(STATUS_SUCCESS
);
610 MiReferenceMemoryArea(IN PMEMORY_AREA MemoryArea
,
614 VALIDATE_MEMORY_AREA(MemoryArea
);
616 InterlockedIncrement(&MemoryArea
->ReferenceCount
);
618 #ifdef TRACK_MEMORY_AREA_REFERENCES
619 DbgPrint("(0x%.08x)(%s:%d) Referencing memory area 0x%.08x (New ref.count %d)\n",
620 KeGetCurrentThread(), FileName
, LineNumber
,
621 MemoryArea
->BaseAddress
,
622 MemoryArea
->ReferenceCount
);
623 #endif /* TRACK_MEMORY_AREA_REFERENCES */
628 MiDereferenceMemoryArea(IN PMEMORY_AREA MemoryArea
,
632 VALIDATE_MEMORY_AREA(MemoryArea
);
634 InterlockedDecrement(&MemoryArea
->ReferenceCount
);
636 #ifdef TRACK_MEMORY_AREA_REFERENCES
637 DbgPrint("(0x%.08x)(%s:%d) Dereferencing memory area 0x%.08x (New ref.count %d)\n",
638 KeGetCurrentThread(), FileName
, LineNumber
,
639 MemoryArea
->BaseAddress
,
640 MemoryArea
->ReferenceCount
);
641 #endif /* TRACK_MEMORY_AREA_REFERENCES */
643 assertmsg(MemoryArea
->ReferenceCount
> 0,
644 ("No outstanding references on memory area (0x%.08x)\n", MemoryArea
));
650 MiReferenceMemoryArea(IN PMEMORY_AREA MemoryArea
)
652 InterlockedIncrement(&MemoryArea
->ReferenceCount
);
657 MiDereferenceMemoryArea(IN PMEMORY_AREA MemoryArea
)
659 InterlockedDecrement(&MemoryArea
->ReferenceCount
);