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
.u
.VadFlags
.Spare
= 1;
173 marea
->VadNode
.u
.VadFlags
.Protection
= MiMakeProtectionMask(marea
->Protect
);
175 /* Build a lame VAD if this is a user-space allocation */
176 if (marea
->VadNode
.EndingVpn
+ 1 < (ULONG_PTR
)MmSystemRangeStart
>> PAGE_SHIFT
)
178 ASSERT(Process
!= NULL
);
179 if (marea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
181 ASSERT(marea
->Type
== MEMORY_AREA_SECTION_VIEW
|| marea
->Type
== MEMORY_AREA_CACHE
);
184 MiLockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
185 MiInsertVad(&marea
->VadNode
, &Process
->VadRoot
);
186 MiUnlockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
187 marea
->Vad
= &marea
->VadNode
;
192 ASSERT(Process
== NULL
);
194 if (!MiRosKernelVadRootInitialized
)
196 MiRosKernelVadRoot
.BalancedRoot
.u1
.Parent
= &MiRosKernelVadRoot
.BalancedRoot
;
197 MiRosKernelVadRoot
.Unused
= 1;
198 MiRosKernelVadRootInitialized
= TRUE
;
202 MiLockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs
);
203 MiInsertVad(&marea
->VadNode
, &MiRosKernelVadRoot
);
204 MiUnlockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs
);
211 PMMSUPPORT AddressSpace
,
213 ULONG_PTR Granularity
,
217 PMM_AVL_TABLE VadRoot
;
218 TABLE_SEARCH_RESULT Result
;
219 PMMADDRESS_NODE Parent
;
220 ULONG_PTR StartingAddress
, HighestAddress
;
222 Process
= MmGetAddressSpaceOwner(AddressSpace
);
223 VadRoot
= Process
? &Process
->VadRoot
: &MiRosKernelVadRoot
;
226 /* Find an address top-down */
227 HighestAddress
= Process
? (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
: (LONG_PTR
)-1;
228 Result
= MiFindEmptyAddressRangeDownTree(Length
,
237 Result
= MiFindEmptyAddressRangeInTree(Length
,
244 if (Result
== TableFoundNode
)
249 return (PVOID
)StartingAddress
;
254 MiRemoveNode(IN PMMADDRESS_NODE Node
,
255 IN PMM_AVL_TABLE Table
);
259 * @name MmFreeMemoryArea
261 * Free an existing memory area.
263 * @param AddressSpace
264 * Address space to free the area from.
266 * Memory area we're about to free.
268 * Callback function for each freed page.
269 * @param FreePageContext
270 * Context passed to the callback function.
274 * @remarks Lock the address space before calling this function.
279 PMMSUPPORT AddressSpace
,
280 PMEMORY_AREA MemoryArea
,
281 PMM_FREE_PAGE_FUNC FreePage
,
282 PVOID FreePageContext
)
287 /* Make sure we own the address space lock! */
288 ASSERT(CONTAINING_RECORD(AddressSpace
, EPROCESS
, Vm
)->AddressCreationLock
.Owner
== KeGetCurrentThread());
291 ASSERT(MemoryArea
->Magic
== 'erAM');
293 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
295 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
296 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
298 if (Process
!= NULL
&&
299 Process
!= CurrentProcess
)
301 KeAttachProcess(&Process
->Pcb
);
304 EndAddress
= MM_ROUND_UP(MA_GetEndingAddress(MemoryArea
), PAGE_SIZE
);
305 for (Address
= MA_GetStartingAddress(MemoryArea
);
306 Address
< (ULONG_PTR
)EndAddress
;
307 Address
+= PAGE_SIZE
)
309 BOOLEAN Dirty
= FALSE
;
310 SWAPENTRY SwapEntry
= 0;
313 if (MmIsPageSwapEntry(Process
, (PVOID
)Address
))
315 MmDeletePageFileMapping(Process
, (PVOID
)Address
, &SwapEntry
);
319 MmDeleteVirtualMapping(Process
, (PVOID
)Address
, &Dirty
, &Page
);
321 if (FreePage
!= NULL
)
323 FreePage(FreePageContext
, MemoryArea
, (PVOID
)Address
,
324 Page
, SwapEntry
, (BOOLEAN
)Dirty
);
326 #if (_MI_PAGING_LEVELS == 2)
327 /* Remove page table reference */
328 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
329 if ((SwapEntry
|| Page
) && ((PVOID
)Address
< MmSystemRangeStart
))
331 ASSERT(AddressSpace
!= MmGetKernelAddressSpace());
332 if (MiQueryPageTableReferences((PVOID
)Address
) == 0)
334 /* No PTE relies on this PDE. Release it */
335 KIRQL OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
336 PMMPDE PointerPde
= MiAddressToPde(Address
);
337 ASSERT(PointerPde
->u
.Hard
.Valid
== 1);
338 MiDeletePte(PointerPde
, MiPdeToPte(PointerPde
), Process
, NULL
);
339 ASSERT(PointerPde
->u
.Hard
.Valid
== 0);
340 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
346 if (Process
!= NULL
&&
347 Process
!= CurrentProcess
)
352 //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT
355 ASSERT(MemoryArea
->VadNode
.EndingVpn
+ 1 < (ULONG_PTR
)MmSystemRangeStart
>> PAGE_SHIFT
);
356 ASSERT(MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
|| MemoryArea
->Type
== MEMORY_AREA_CACHE
);
358 /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
359 ASSERT(MemoryArea
->VadNode
.u
.VadFlags
.Spare
!= 0);
360 if (((PMMVAD
)MemoryArea
->Vad
)->u
.VadFlags
.Spare
== 1)
362 MiRemoveNode((PMMADDRESS_NODE
)&MemoryArea
->VadNode
, &Process
->VadRoot
);
365 MemoryArea
->Vad
= NULL
;
369 MiRemoveNode((PMMADDRESS_NODE
)&MemoryArea
->VadNode
, &MiRosKernelVadRoot
);
374 MemoryArea
->Magic
= 'daeD';
376 ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
378 DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
380 return STATUS_SUCCESS
;
384 * @name MmCreateMemoryArea
386 * Create a memory area.
388 * @param AddressSpace
389 * Address space to create the area in.
391 * Type of the memory area.
393 * Base address for the memory area we're about the create. On
394 * input it contains either 0 (auto-assign address) or preferred
395 * address. On output it contains the starting address of the
396 * newly created area.
398 * Length of the area to allocate.
400 * Protection attributes for the memory area.
402 * Receives a pointer to the memory area on successful exit.
406 * @remarks Lock the address space before calling this function.
410 MmCreateMemoryArea(PMMSUPPORT AddressSpace
,
415 PMEMORY_AREA
*Result
,
416 ULONG AllocationFlags
,
420 PMEMORY_AREA MemoryArea
;
421 ULONG_PTR EndingAddress
;
423 DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
424 "*BaseAddress %p, Length %p, AllocationFlags %x, "
426 Type
, BaseAddress
, *BaseAddress
, Length
, AllocationFlags
,
429 /* Is this a static memory area? */
430 if (Type
& MEMORY_AREA_STATIC
)
432 /* Use the static array instead of the pool */
433 ASSERT(MiStaticMemoryAreaCount
< MI_STATIC_MEMORY_AREAS
);
434 MemoryArea
= &MiStaticMemoryAreas
[MiStaticMemoryAreaCount
++];
438 /* Allocate the memory area from nonpaged pool */
439 MemoryArea
= ExAllocatePoolWithTag(NonPagedPool
,
446 DPRINT1("Not enough memory.\n");
447 return STATUS_NO_MEMORY
;
450 RtlZeroMemory(MemoryArea
, sizeof(MEMORY_AREA
));
451 MemoryArea
->Type
= Type
& ~MEMORY_AREA_STATIC
;
452 MemoryArea
->Protect
= Protect
;
453 MemoryArea
->Flags
= AllocationFlags
;
454 MemoryArea
->Magic
= 'erAM';
455 MemoryArea
->DeleteInProgress
= FALSE
;
457 if (*BaseAddress
== 0)
459 tmpLength
= (ULONG_PTR
)MM_ROUND_UP(Length
, PAGE_SIZE
);
460 *BaseAddress
= MmFindGap(AddressSpace
,
463 (AllocationFlags
& MEM_TOP_DOWN
) == MEM_TOP_DOWN
);
464 if ((*BaseAddress
) == 0)
466 DPRINT("No suitable gap\n");
467 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
468 return STATUS_NO_MEMORY
;
471 MemoryArea
->VadNode
.StartingVpn
= (ULONG_PTR
)*BaseAddress
>> PAGE_SHIFT
;
472 MemoryArea
->VadNode
.EndingVpn
= ((ULONG_PTR
)*BaseAddress
+ tmpLength
- 1) >> PAGE_SHIFT
;
473 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
477 EndingAddress
= ((ULONG_PTR
)*BaseAddress
+ Length
- 1) | (PAGE_SIZE
- 1);
478 *BaseAddress
= ALIGN_DOWN_POINTER_BY(*BaseAddress
, Granularity
);
479 tmpLength
= EndingAddress
+ 1 - (ULONG_PTR
)*BaseAddress
;
481 if (!MmGetAddressSpaceOwner(AddressSpace
) && *BaseAddress
< MmSystemRangeStart
)
484 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
485 return STATUS_ACCESS_VIOLATION
;
488 if (MmGetAddressSpaceOwner(AddressSpace
) &&
489 (ULONG_PTR
)(*BaseAddress
) + tmpLength
> (ULONG_PTR
)MmSystemRangeStart
)
491 DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
492 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
493 return STATUS_ACCESS_VIOLATION
;
496 /* No need to check ARM3 owned memory areas, the range MUST be free */
497 if (MemoryArea
->Type
!= MEMORY_AREA_OWNED_BY_ARM3
)
499 if (MmLocateMemoryAreaByRegion(AddressSpace
,
503 DPRINT("Memory area already occupied\n");
504 if (!(Type
& MEMORY_AREA_STATIC
)) ExFreePoolWithTag(MemoryArea
, TAG_MAREA
);
505 return STATUS_CONFLICTING_ADDRESSES
;
509 MemoryArea
->VadNode
.StartingVpn
= (ULONG_PTR
)*BaseAddress
>> PAGE_SHIFT
;
510 MemoryArea
->VadNode
.EndingVpn
= ((ULONG_PTR
)*BaseAddress
+ tmpLength
- 1) >> PAGE_SHIFT
;
511 MmInsertMemoryArea(AddressSpace
, MemoryArea
);
514 *Result
= MemoryArea
;
516 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress
);
517 return STATUS_SUCCESS
;
522 MiRosCleanupMemoryArea(
526 PMEMORY_AREA MemoryArea
;
530 /* We must be called from MmCleanupAddressSpace and nowhere else!
531 Make sure things are as expected... */
532 ASSERT(Process
== PsGetCurrentProcess());
533 ASSERT(Process
->VmDeleted
== TRUE
);
534 ASSERT(((PsGetCurrentThread()->ThreadsProcess
== Process
) &&
535 (Process
->ActiveThreads
== 1)) ||
536 (Process
->ActiveThreads
== 0));
538 /* We are in cleanup, we don't need to synchronize */
539 MmUnlockAddressSpace(&Process
->Vm
);
541 MemoryArea
= (PMEMORY_AREA
)Vad
;
542 BaseAddress
= (PVOID
)MA_GetStartingAddress(MemoryArea
);
544 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
546 Status
= MiRosUnmapViewOfSection(Process
, BaseAddress
, Process
->ProcessExiting
);
548 else if (MemoryArea
->Type
== MEMORY_AREA_CACHE
)
550 Status
= MmUnmapViewOfCacheSegment(&Process
->Vm
, BaseAddress
);
554 /* There shouldn't be anything else! */
558 /* Make sure this worked! */
559 ASSERT(NT_SUCCESS(Status
));
561 /* Lock the address space again */
562 MmLockAddressSpace(&Process
->Vm
);
567 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
);
571 MmDeleteProcessAddressSpace(PEPROCESS Process
)
576 DPRINT("MmDeleteProcessAddressSpace(Process %p (%s))\n", Process
,
577 Process
->ImageFileName
);
580 OldIrql
= MiAcquireExpansionLock();
581 RemoveEntryList(&Process
->MmProcessLinks
);
582 MiReleaseExpansionLock(OldIrql
);
584 MmLockAddressSpace(&Process
->Vm
);
586 /* There should not be any memory areas left! */
587 ASSERT(Process
->Vm
.WorkingSetExpansionLinks
.Flink
== NULL
);
589 #if (_MI_PAGING_LEVELS == 2)
593 /* Attach to Process */
594 KeAttachProcess(&Process
->Pcb
);
596 /* Acquire PFN lock */
597 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
599 for (Address
= MI_LOWEST_VAD_ADDRESS
;
600 Address
< MM_HIGHEST_VAD_ADDRESS
;
601 Address
=(PVOID
)((ULONG_PTR
)Address
+ (PAGE_SIZE
* PTE_COUNT
)))
603 /* At this point all references should be dead */
604 if (MiQueryPageTableReferences(Address
) != 0)
606 DPRINT1("Process %p, Address %p, UsedPageTableEntries %lu\n",
609 MiQueryPageTableReferences(Address
));
610 ASSERT(MiQueryPageTableReferences(Address
) == 0);
613 pointerPde
= MiAddressToPde(Address
);
614 /* Unlike in ARM3, we don't necesarrily free the PDE page as soon as reference reaches 0,
615 * so we must clean up a bit when process closes */
616 if (pointerPde
->u
.Hard
.Valid
)
617 MiDeletePte(pointerPde
, MiPdeToPte(pointerPde
), Process
, NULL
);
618 ASSERT(pointerPde
->u
.Hard
.Valid
== 0);
622 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
629 MmUnlockAddressSpace(&Process
->Vm
);
631 DPRINT("Finished MmDeleteProcessAddressSpace()\n");
632 MmDeleteProcessAddressSpace2(Process
);
633 return(STATUS_SUCCESS
);