2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/marea.c
21 * PURPOSE: Implements memory areas
23 * PROGRAMMERS: Rex Jolliff
33 * Thomas Weidenmueller
34 * Gunnar Andre' Dalsnes
42 /* INCLUDES *****************************************************************/
46 #include <cache/section/newmm.h>
49 #include "ARM3/miarm.h"
51 MEMORY_AREA MiStaticMemoryAreas
[MI_STATIC_MEMORY_AREAS
];
52 ULONG MiStaticMemoryAreaCount
;
54 MM_AVL_TABLE MiRosKernelVadRoot
;
55 BOOLEAN MiRosKernelVadRootInitialized
;
57 /* FUNCTIONS *****************************************************************/
60 MmLocateMemoryAreaByAddress(
61 PMMSUPPORT AddressSpace
,
64 ULONG_PTR StartVpn
= (ULONG_PTR
)Address_
/ PAGE_SIZE
;
68 PMEMORY_AREA MemoryArea
;
69 TABLE_SEARCH_RESULT Result
;
72 Process
= MmGetAddressSpaceOwner(AddressSpace
);
73 Table
= (Process
!= NULL
) ? &Process
->VadRoot
: &MiRosKernelVadRoot
;
75 Result
= MiCheckForConflictingNode(StartVpn
, StartVpn
, Table
, &Node
);
76 if (Result
!= TableFoundNode
)
81 Vad
= (PMMVAD_LONG
)Node
;
82 if (Vad
->u
.VadFlags
.Spare
== 0)
84 /* Check if this is VM VAD */
85 if (Vad
->ControlArea
== NULL
)
87 /* We store the reactos MEMORY_AREA here */
88 MemoryArea
= (PMEMORY_AREA
)Vad
->FirstPrototypePte
;
92 /* This is a section VAD. Store the MAREA here for now */
93 MemoryArea
= (PMEMORY_AREA
)Vad
->u4
.Banked
;
98 MemoryArea
= (PMEMORY_AREA
)Node
;
106 MmLocateMemoryAreaByRegion(
107 PMMSUPPORT AddressSpace
,
111 ULONG_PTR StartVpn
= (ULONG_PTR
)Address_
/ PAGE_SIZE
;
112 ULONG_PTR EndVpn
= ((ULONG_PTR
)Address_
+ Length
- 1) / PAGE_SIZE
;
115 PMMADDRESS_NODE Node
;
116 PMEMORY_AREA MemoryArea
;
117 TABLE_SEARCH_RESULT Result
;
120 Process
= MmGetAddressSpaceOwner(AddressSpace
);
121 Table
= (Process
!= NULL
) ? &Process
->VadRoot
: &MiRosKernelVadRoot
;
123 Result
= MiCheckForConflictingNode(StartVpn
, EndVpn
, Table
, &Node
);
124 if (Result
!= TableFoundNode
)
129 Vad
= (PMMVAD_LONG
)Node
;
130 if (Vad
->u
.VadFlags
.Spare
== 0)
132 /* Check if this is VM VAD */
133 if (Vad
->ControlArea
== NULL
)
135 /* We store the reactos MEMORY_AREA here */
136 MemoryArea
= (PMEMORY_AREA
)Vad
->FirstPrototypePte
;
140 /* This is a section VAD. Store the MAREA here for now */
141 MemoryArea
= (PMEMORY_AREA
)Vad
->u4
.Banked
;
146 MemoryArea
= (PMEMORY_AREA
)Node
;
149 ASSERT(MemoryArea
!= NULL
);
155 MiInsertVad(IN PMMVAD Vad
,
156 IN PMM_AVL_TABLE VadRoot
);
160 MiMakeProtectionMask(
167 PMMSUPPORT AddressSpace
,
170 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
172 marea
->VadNode
.StartingVpn
= marea
->StartingVpn
;
173 marea
->VadNode
.EndingVpn
= marea
->EndingVpn
;
174 marea
->VadNode
.u
.VadFlags
.Spare
= 1;
175 marea
->VadNode
.u
.VadFlags
.Protection
= MiMakeProtectionMask(marea
->Protect
);
177 /* Build a lame VAD if this is a user-space allocation */
178 if (marea
->EndingVpn
+ 1 < (ULONG_PTR
)MmSystemRangeStart
>> PAGE_SHIFT
)
180 ASSERT(Process
!= NULL
);
181 if (marea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
183 ASSERT(marea
->Type
== MEMORY_AREA_SECTION_VIEW
|| marea
->Type
== MEMORY_AREA_CACHE
);
186 MiLockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
187 MiInsertVad(&marea
->VadNode
, &Process
->VadRoot
);
188 MiUnlockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
189 marea
->Vad
= &marea
->VadNode
;
194 ASSERT(Process
== NULL
);
196 if (!MiRosKernelVadRootInitialized
)
198 MiRosKernelVadRoot
.BalancedRoot
.u1
.Parent
= &MiRosKernelVadRoot
.BalancedRoot
;
199 MiRosKernelVadRoot
.Unused
= 1;
200 MiRosKernelVadRootInitialized
= TRUE
;
204 MiLockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs
);
205 MiInsertVad(&marea
->VadNode
, &MiRosKernelVadRoot
);
206 MiUnlockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs
);
213 PMMSUPPORT AddressSpace
,
215 ULONG_PTR Granularity
,
219 PMM_AVL_TABLE VadRoot
;
220 TABLE_SEARCH_RESULT Result
;
221 PMMADDRESS_NODE Parent
;
222 ULONG_PTR StartingAddress
, HighestAddress
;
224 Process
= MmGetAddressSpaceOwner(AddressSpace
);
225 VadRoot
= Process
? &Process
->VadRoot
: &MiRosKernelVadRoot
;
228 /* Find an address top-down */
229 HighestAddress
= Process
? (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
: (LONG_PTR
)-1;
230 Result
= MiFindEmptyAddressRangeDownTree(Length
,
239 Result
= MiFindEmptyAddressRangeInTree(Length
,
246 if (Result
== TableFoundNode
)
251 return (PVOID
)StartingAddress
;
256 MiRemoveNode(IN PMMADDRESS_NODE Node
,
257 IN PMM_AVL_TABLE Table
);
261 * @name MmFreeMemoryArea
263 * Free an existing memory area.
265 * @param AddressSpace
266 * Address space to free the area from.
268 * Memory area we're about to free.
270 * Callback function for each freed page.
271 * @param FreePageContext
272 * Context passed to the callback function.
276 * @remarks Lock the address space before calling this function.
281 PMMSUPPORT AddressSpace
,
282 PMEMORY_AREA MemoryArea
,
283 PMM_FREE_PAGE_FUNC FreePage
,
284 PVOID FreePageContext
)
289 /* Make sure we own the address space lock! */
290 ASSERT(CONTAINING_RECORD(AddressSpace
, EPROCESS
, Vm
)->AddressCreationLock
.Owner
== KeGetCurrentThread());
293 ASSERT(MemoryArea
->Magic
== 'erAM');
295 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
297 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
298 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
300 if (Process
!= NULL
&&
301 Process
!= CurrentProcess
)
303 KeAttachProcess(&Process
->Pcb
);
306 EndAddress
= MM_ROUND_UP(MA_GetEndingAddress(MemoryArea
), PAGE_SIZE
);
307 for (Address
= MA_GetStartingAddress(MemoryArea
);
308 Address
< (ULONG_PTR
)EndAddress
;
309 Address
+= PAGE_SIZE
)
311 BOOLEAN Dirty
= FALSE
;
312 SWAPENTRY SwapEntry
= 0;
315 if (MmIsPageSwapEntry(Process
, (PVOID
)Address
))
317 MmDeletePageFileMapping(Process
, (PVOID
)Address
, &SwapEntry
);
321 MmDeleteVirtualMapping(Process
, (PVOID
)Address
, &Dirty
, &Page
);
323 if (FreePage
!= NULL
)
325 FreePage(FreePageContext
, MemoryArea
, (PVOID
)Address
,
326 Page
, SwapEntry
, (BOOLEAN
)Dirty
);
328 #if (_MI_PAGING_LEVELS == 2)
329 /* Remove page table reference */
330 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
331 if ((SwapEntry
|| Page
) && ((PVOID
)Address
< MmSystemRangeStart
))
333 ASSERT(AddressSpace
!= MmGetKernelAddressSpace());
334 if (MiQueryPageTableReferences((PVOID
)Address
) == 0)
336 /* No PTE relies on this PDE. Release it */
337 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
338 PMMPDE PointerPde
= MiAddressToPde(Address
);
339 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
340 MiDeletePte(PointerPde
, MiPdeToPte(PointerPde
), Process
, NULL
);
341 ASSERT(PointerPde
->u
.Hard
.Valid
== 0);
342 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
348 if (Process
!= NULL
&&
349 Process
!= CurrentProcess
)
354 //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT
357 ASSERT(MemoryArea
->EndingVpn
+ 1 < (ULONG_PTR
)MmSystemRangeStart
>> PAGE_SHIFT
);
358 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
|| MemoryArea
->Type
== MEMORY_AREA_CACHE
);
360 /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
361 ASSERT(MemoryArea
->VadNode
.u
.VadFlags
.Spare
!= 0);
362 if (((PMMVAD
)MemoryArea
->Vad
)->u
.VadFlags
.Spare
== 1)
364 MiRemoveNode((PMMADDRESS_NODE
)&MemoryArea
->VadNode
, &Process
->VadRoot
);
367 MemoryArea
->Vad
= NULL
;
371 MiRemoveNode((PMMADDRESS_NODE
)&MemoryArea
->VadNode
, &MiRosKernelVadRoot
);
376 MemoryArea
->Magic
= 'daeD';
378 ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
380 DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
382 return STATUS_SUCCESS
;
386 * @name MmCreateMemoryArea
388 * Create a memory area.
390 * @param AddressSpace
391 * Address space to create the area in.
393 * Type of the memory area.
395 * Base address for the memory area we're about the create. On
396 * input it contains either 0 (auto-assign address) or preferred
397 * address. On output it contains the starting address of the
398 * newly created area.
400 * Length of the area to allocate.
402 * Protection attributes for the memory area.
404 * Receives a pointer to the memory area on successful exit.
408 * @remarks Lock the address space before calling this function.
412 MmCreateMemoryArea(PMMSUPPORT AddressSpace
,
417 PMEMORY_AREA
*Result
,
418 ULONG AllocationFlags
,
422 PMEMORY_AREA MemoryArea
;
423 ULONG_PTR EndingAddress
;
425 DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
426 "*BaseAddress %p, Length %p, AllocationFlags %x, "
428 Type
, BaseAddress
, *BaseAddress
, Length
, AllocationFlags
,
431 /* Is this a static memory area? */
432 if (Type
& MEMORY_AREA_STATIC
)
434 /* Use the static array instead of the pool */
435 ASSERT(MiStaticMemoryAreaCount
< MI_STATIC_MEMORY_AREAS
);
436 MemoryArea
= &MiStaticMemoryAreas
[MiStaticMemoryAreaCount
++];
440 /* Allocate the memory area from nonpaged pool */
441 MemoryArea
= ExAllocatePoolWithTag(NonPagedPool
,
448 DPRINT1("Not enough memory.\n");
449 return STATUS_NO_MEMORY
;
452 RtlZeroMemory(MemoryArea
, sizeof(MEMORY_AREA
));
453 MemoryArea
->Type
= Type
& ~MEMORY_AREA_STATIC
;
454 MemoryArea
->Protect
= Protect
;
455 MemoryArea
->Flags
= AllocationFlags
;
456 MemoryArea
->Magic
= 'erAM';
457 MemoryArea
->DeleteInProgress
= FALSE
;
459 if (*BaseAddress
== 0)
461 tmpLength
= (ULONG_PTR
)MM_ROUND_UP(Length
, PAGE_SIZE
);
462 *BaseAddress
= MmFindGap(AddressSpace
,
465 (AllocationFlags
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
);
466 if ((*BaseAddress
) == 0)
468 DPRINT("No suitable gap\n");
469 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
470 return STATUS_NO_MEMORY
;
473 MemoryArea
->StartingVpn
= (ULONG_PTR
)*BaseAddress
>> PAGE_SHIFT
;
474 MemoryArea
->EndingVpn
= ((ULONG_PTR
)*BaseAddress
+ tmpLength
- 1) >> PAGE_SHIFT
;
475 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
479 EndingAddress
= ((ULONG_PTR
)*BaseAddress
+ Length
- 1) | (PAGE_SIZE
- 1);
480 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, Granularity
);
481 tmpLength
= EndingAddress
+ 1 - (ULONG_PTR
)*BaseAddress
;
483 if (!MmGetAddressSpaceOwner(AddressSpace
) && *BaseAddress
< MmSystemRangeStart
)
486 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
487 return STATUS_ACCESS_VIOLATION
;
490 if (MmGetAddressSpaceOwner(AddressSpace
) &&
491 (ULONG_PTR
)(*BaseAddress
) + tmpLength
> (ULONG_PTR
)MmSystemRangeStart
)
493 DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
494 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
495 return STATUS_ACCESS_VIOLATION
;
498 /* No need to check ARM3 owned memory areas, the range MUST be free */
499 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
501 if (MmLocateMemoryAreaByRegion(AddressSpace
,
505 DPRINT("Memory area already occupied\n");
506 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
507 return STATUS_CONFLICTING_ADDRESSES
;
511 MemoryArea
->StartingVpn
= (ULONG_PTR
)*BaseAddress
>> PAGE_SHIFT
;
512 MemoryArea
->EndingVpn
= ((ULONG_PTR
)*BaseAddress
+ tmpLength
- 1) >> PAGE_SHIFT
;
513 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
516 *Result
= MemoryArea
;
518 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress
);
519 return STATUS_SUCCESS
;
524 MiRosCleanupMemoryArea(
528 PMEMORY_AREA MemoryArea
;
532 /* We must be called from MmCleanupAddressSpace and nowhere else!
533 Make sure things are as expected... */
534 ASSERT(Process
== PsGetCurrentProcess());
535 ASSERT(Process
->VmDeleted
== TRUE
);
536 ASSERT(((PsGetCurrentThread()->ThreadsProcess
== Process
) &&
537 (Process
->ActiveThreads
== 1)) ||
538 (Process
->ActiveThreads
== 0));
540 /* We are in cleanup, we don't need to synchronize */
541 MmUnlockAddressSpace(&Process
->Vm
);
543 MemoryArea
= (PMEMORY_AREA
)Vad
;
544 BaseAddress
= (PVOID
)MA_GetStartingAddress(MemoryArea
);
546 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
548 Status
= MiRosUnmapViewOfSection(Process
, BaseAddress
, 0);
550 else if (MemoryArea
->Type
== MEMORY_AREA_CACHE
)
552 Status
= MmUnmapViewOfCacheSegment(&Process
->Vm
, BaseAddress
);
556 /* There shouldn't be anything else! */
560 /* Make sure this worked! */
561 ASSERT(NT_SUCCESS(Status
));
563 /* Lock the address space again */
564 MmLockAddressSpace(&Process
->Vm
);
569 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
);
573 MmDeleteProcessAddressSpace(PEPROCESS Process
)
578 DPRINT("MmDeleteProcessAddressSpace(Process %p (%s))\n", Process
,
579 Process
->ImageFileName
);
582 OldIrql
= MiAcquireExpansionLock();
583 RemoveEntryList(&Process
->MmProcessLinks
);
584 MiReleaseExpansionLock(OldIrql
);
586 MmLockAddressSpace(&Process
->Vm
);
588 /* There should not be any memory areas left! */
589 ASSERT(Process
->Vm
.WorkingSetExpansionLinks
.Flink
== NULL
);
591 #if (_MI_PAGING_LEVELS == 2)
595 /* Attach to Process */
596 KeAttachProcess(&Process
->Pcb
);
598 /* Acquire PFN lock */
599 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
601 for (Address
= MI_LOWEST_VAD_ADDRESS
;
602 Address
< MM_HIGHEST_VAD_ADDRESS
;
603 Address
=(PVOID
)((ULONG_PTR
)Address
+ (PAGE_SIZE
* PTE_COUNT
)))
605 /* At this point all references should be dead */
606 if (MiQueryPageTableReferences(Address
) != 0)
608 DPRINT1("Process %p, Address %p, UsedPageTableEntries %lu\n",
611 MiQueryPageTableReferences(Address
));
612 ASSERT(MiQueryPageTableReferences(Address
) == 0);
615 pointerPde
= MiAddressToPde(Address
);
616 /* Unlike in ARM3, we don't necesarrily free the PDE page as soon as reference reaches 0,
617 * so we must clean up a bit when process closes */
618 if (pointerPde
->u
.Hard
.Valid
)
619 MiDeletePte(pointerPde
, MiPdeToPte(pointerPde
), Process
, NULL
);
620 ASSERT(pointerPde
->u
.Hard
.Valid
== 0);
624 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
631 MmUnlockAddressSpace(&Process
->Vm
);
633 DPRINT("Finished MmDeleteProcessAddressSpace()\n");
634 MmDeleteProcessAddressSpace2(Process
);
635 return(STATUS_SUCCESS
);