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 (MA_GetEndingAddress(marea
) < (ULONG_PTR
)MmSystemRangeStart
)
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.
280 MiDeletePte(IN PMMPTE PointerPte
,
281 IN PVOID VirtualAddress
,
282 IN PEPROCESS CurrentProcess
,
283 IN PMMPTE PrototypePte
);
287 PMMSUPPORT AddressSpace
,
288 PMEMORY_AREA MemoryArea
,
289 PMM_FREE_PAGE_FUNC FreePage
,
290 PVOID FreePageContext
)
295 /* Make sure we own the address space lock! */
296 ASSERT(CONTAINING_RECORD(AddressSpace
, EPROCESS
, Vm
)->AddressCreationLock
.Owner
== KeGetCurrentThread());
299 ASSERT(MemoryArea
->Magic
== 'erAM');
301 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
303 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
304 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
306 if (Process
!= NULL
&&
307 Process
!= CurrentProcess
)
309 KeAttachProcess(&Process
->Pcb
);
312 EndAddress
= MM_ROUND_UP(MA_GetEndingAddress(MemoryArea
), PAGE_SIZE
);
313 for (Address
= MA_GetStartingAddress(MemoryArea
);
314 Address
< (ULONG_PTR
)EndAddress
;
315 Address
+= PAGE_SIZE
)
317 BOOLEAN Dirty
= FALSE
;
318 SWAPENTRY SwapEntry
= 0;
321 if (MmIsPageSwapEntry(Process
, (PVOID
)Address
))
323 MmDeletePageFileMapping(Process
, (PVOID
)Address
, &SwapEntry
);
327 MmDeleteVirtualMapping(Process
, (PVOID
)Address
, &Dirty
, &Page
);
329 if (FreePage
!= NULL
)
331 FreePage(FreePageContext
, MemoryArea
, (PVOID
)Address
,
332 Page
, SwapEntry
, (BOOLEAN
)Dirty
);
334 #if (_MI_PAGING_LEVELS == 2)
335 /* Remove page table reference */
336 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
337 if ((SwapEntry
|| Page
) && ((PVOID
)Address
< MmSystemRangeStart
))
339 ASSERT(AddressSpace
!= MmGetKernelAddressSpace());
340 if (MiQueryPageTableReferences((PVOID
)Address
) == 0)
342 /* No PTE relies on this PDE. Release it */
343 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
344 PMMPDE PointerPde
= MiAddressToPde(Address
);
345 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
346 MiDeletePte(PointerPde
, MiPdeToPte(PointerPde
), Process
, NULL
);
347 ASSERT(PointerPde
->u
.Hard
.Valid
== 0);
348 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
354 if (Process
!= NULL
&&
355 Process
!= CurrentProcess
)
360 //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT
363 ASSERT(MA_GetEndingAddress(MemoryArea
) < (ULONG_PTR
)MmSystemRangeStart
);
364 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
|| MemoryArea
->Type
== MEMORY_AREA_CACHE
);
366 /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
367 ASSERT(MemoryArea
->VadNode
.u
.VadFlags
.Spare
!= 0);
368 if (((PMMVAD
)MemoryArea
->Vad
)->u
.VadFlags
.Spare
== 1)
370 MiRemoveNode((PMMADDRESS_NODE
)&MemoryArea
->VadNode
, &Process
->VadRoot
);
373 MemoryArea
->Vad
= NULL
;
377 MiRemoveNode((PMMADDRESS_NODE
)&MemoryArea
->VadNode
, &MiRosKernelVadRoot
);
381 ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
383 DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
385 return STATUS_SUCCESS
;
389 * @name MmCreateMemoryArea
391 * Create a memory area.
393 * @param AddressSpace
394 * Address space to create the area in.
396 * Type of the memory area.
398 * Base address for the memory area we're about the create. On
399 * input it contains either 0 (auto-assign address) or preferred
400 * address. On output it contains the starting address of the
401 * newly created area.
403 * Length of the area to allocate.
405 * Protection attributes for the memory area.
407 * Receives a pointer to the memory area on successful exit.
411 * @remarks Lock the address space before calling this function.
415 MmCreateMemoryArea(PMMSUPPORT AddressSpace
,
420 PMEMORY_AREA
*Result
,
421 ULONG AllocationFlags
,
425 PMEMORY_AREA MemoryArea
;
426 ULONG_PTR EndingAddress
;
428 DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
429 "*BaseAddress %p, Length %p, AllocationFlags %x, "
431 Type
, BaseAddress
, *BaseAddress
, Length
, AllocationFlags
,
434 /* Is this a static memory area? */
435 if (Type
& MEMORY_AREA_STATIC
)
437 /* Use the static array instead of the pool */
438 ASSERT(MiStaticMemoryAreaCount
< MI_STATIC_MEMORY_AREAS
);
439 MemoryArea
= &MiStaticMemoryAreas
[MiStaticMemoryAreaCount
++];
443 /* Allocate the memory area from nonpaged pool */
444 MemoryArea
= ExAllocatePoolWithTag(NonPagedPool
,
451 DPRINT1("Not enough memory.\n");
452 return STATUS_NO_MEMORY
;
455 RtlZeroMemory(MemoryArea
, sizeof(MEMORY_AREA
));
456 MemoryArea
->Type
= Type
& ~MEMORY_AREA_STATIC
;
457 MemoryArea
->Protect
= Protect
;
458 MemoryArea
->Flags
= AllocationFlags
;
459 MemoryArea
->Magic
= 'erAM';
460 MemoryArea
->DeleteInProgress
= FALSE
;
462 if (*BaseAddress
== 0)
464 tmpLength
= (ULONG_PTR
)MM_ROUND_UP(Length
, PAGE_SIZE
);
465 *BaseAddress
= MmFindGap(AddressSpace
,
468 (AllocationFlags
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
);
469 if ((*BaseAddress
) == 0)
471 DPRINT("No suitable gap\n");
472 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
473 return STATUS_NO_MEMORY
;
476 MemoryArea
->StartingVpn
= (ULONG_PTR
)*BaseAddress
>> PAGE_SHIFT
;
477 MemoryArea
->EndingVpn
= ((ULONG_PTR
)*BaseAddress
+ tmpLength
- 1) >> PAGE_SHIFT
;
478 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
482 EndingAddress
= ((ULONG_PTR
)*BaseAddress
+ Length
- 1) | (PAGE_SIZE
- 1);
483 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, Granularity
);
484 tmpLength
= EndingAddress
+ 1 - (ULONG_PTR
)*BaseAddress
;
486 if (!MmGetAddressSpaceOwner(AddressSpace
) && *BaseAddress
< MmSystemRangeStart
)
489 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
490 return STATUS_ACCESS_VIOLATION
;
493 if (MmGetAddressSpaceOwner(AddressSpace
) &&
494 (ULONG_PTR
)(*BaseAddress
) + tmpLength
> (ULONG_PTR
)MmSystemRangeStart
)
496 DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
497 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
498 return STATUS_ACCESS_VIOLATION
;
501 /* No need to check ARM3 owned memory areas, the range MUST be free */
502 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
504 if (MmLocateMemoryAreaByRegion(AddressSpace
,
508 DPRINT("Memory area already occupied\n");
509 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
510 return STATUS_CONFLICTING_ADDRESSES
;
514 MemoryArea
->StartingVpn
= (ULONG_PTR
)*BaseAddress
>> PAGE_SHIFT
;
515 MemoryArea
->EndingVpn
= ((ULONG_PTR
)*BaseAddress
+ tmpLength
- 1) >> PAGE_SHIFT
;
516 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
519 *Result
= MemoryArea
;
521 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress
);
522 return STATUS_SUCCESS
;
527 MiRosCleanupMemoryArea(
531 PMEMORY_AREA MemoryArea
;
535 /* We must be called from MmCleanupAddressSpace and nowhere else!
536 Make sure things are as expected... */
537 ASSERT(Process
== PsGetCurrentProcess());
538 ASSERT(Process
->VmDeleted
== TRUE
);
539 ASSERT(((PsGetCurrentThread()->ThreadsProcess
== Process
) &&
540 (Process
->ActiveThreads
== 1)) ||
541 (Process
->ActiveThreads
== 0));
543 /* We are in cleanup, we don't need to synchronize */
544 MmUnlockAddressSpace(&Process
->Vm
);
546 MemoryArea
= (PMEMORY_AREA
)Vad
;
547 BaseAddress
= (PVOID
)MA_GetStartingAddress(MemoryArea
);
549 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
551 Status
= MiRosUnmapViewOfSection(Process
, BaseAddress
, 0);
553 else if (MemoryArea
->Type
== MEMORY_AREA_CACHE
)
555 Status
= MmUnmapViewOfCacheSegment(&Process
->Vm
, BaseAddress
);
559 /* There shouldn't be anything else! */
563 /* Make sure this worked! */
564 ASSERT(NT_SUCCESS(Status
));
566 /* Lock the address space again */
567 MmLockAddressSpace(&Process
->Vm
);
572 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
);
576 MmDeleteProcessAddressSpace(PEPROCESS Process
)
580 DPRINT("MmDeleteProcessAddressSpace(Process %p (%s))\n", Process
,
581 Process
->ImageFileName
);
584 RemoveEntryList(&Process
->MmProcessLinks
);
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
);