ffb8efe582d2ab1e87f4b082a3f897a79a6d8433
[reactos.git] / reactos / ntoskrnl / mm / marea.c
1 /*
2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3 *
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.
8 *
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.
13 *
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.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/marea.c
21 * PURPOSE: Implements memory areas
22 *
23 * PROGRAMMERS: Rex Jolliff
24 * David Welch
25 * Eric Kohl
26 * Philip Susi
27 * Casper Hornstrup
28 * Eric Kohl
29 * Ge van Geldorp
30 * Royce Mitchell III
31 * Aleksey Bragin
32 * Jason Filby
33 * Thomas Weidenmueller
34 * Gunnar Andre' Dalsnes
35 * Mike Nordell
36 * Alex Ionescu
37 * Filip Navara
38 * Herve Poussineau
39 * Steven Edwards
40 */
41
42 /* INCLUDES *****************************************************************/
43
44 #include <ntoskrnl.h>
45 #define NDEBUG
46 #include <cache/section/newmm.h>
47 #include <debug.h>
48
49 #include "ARM3/miarm.h"
50
51 MEMORY_AREA MiStaticMemoryAreas[MI_STATIC_MEMORY_AREAS];
52 ULONG MiStaticMemoryAreaCount;
53
54 MM_AVL_TABLE MiRosKernelVadRoot;
55 BOOLEAN MiRosKernelVadRootInitialized;
56
57 /* FUNCTIONS *****************************************************************/
58
59 PMEMORY_AREA NTAPI
60 MmLocateMemoryAreaByAddress(
61 PMMSUPPORT AddressSpace,
62 PVOID Address_)
63 {
64 ULONG_PTR StartVpn = (ULONG_PTR)Address_ / PAGE_SIZE;
65 PEPROCESS Process;
66 PMM_AVL_TABLE Table;
67 PMMADDRESS_NODE Node;
68 PMEMORY_AREA MemoryArea;
69 TABLE_SEARCH_RESULT Result;
70 PMMVAD_LONG Vad;
71
72 Process = MmGetAddressSpaceOwner(AddressSpace);
73 Table = (Process != NULL) ? &Process->VadRoot : &MiRosKernelVadRoot;
74
75 Result = MiCheckForConflictingNode(StartVpn, StartVpn, Table, &Node);
76 if (Result != TableFoundNode)
77 {
78 return NULL;
79 }
80
81 Vad = (PMMVAD_LONG)Node;
82 if (Vad->u.VadFlags.Spare == 0)
83 {
84 /* Check if this is VM VAD */
85 if (Vad->ControlArea == NULL)
86 {
87 /* We store the reactos MEMORY_AREA here */
88 MemoryArea = (PMEMORY_AREA)Vad->FirstPrototypePte;
89 }
90 else
91 {
92 /* This is a section VAD. Store the MAREA here for now */
93 MemoryArea = (PMEMORY_AREA)Vad->u4.Banked;
94 }
95 }
96 else
97 {
98 MemoryArea = (PMEMORY_AREA)Node;
99 }
100
101 return MemoryArea;
102 }
103
104 PMEMORY_AREA
105 NTAPI
106 MmLocateMemoryAreaByRegion(
107 PMMSUPPORT AddressSpace,
108 PVOID Address_,
109 ULONG_PTR Length)
110 {
111 ULONG_PTR StartVpn = (ULONG_PTR)Address_ / PAGE_SIZE;
112 ULONG_PTR EndVpn = ((ULONG_PTR)Address_ + Length - 1) / PAGE_SIZE;
113 PEPROCESS Process;
114 PMM_AVL_TABLE Table;
115 PMMADDRESS_NODE Node;
116 PMEMORY_AREA MemoryArea;
117 TABLE_SEARCH_RESULT Result;
118 PMMVAD_LONG Vad;
119
120 Process = MmGetAddressSpaceOwner(AddressSpace);
121 Table = (Process != NULL) ? &Process->VadRoot : &MiRosKernelVadRoot;
122
123 Result = MiCheckForConflictingNode(StartVpn, EndVpn, Table, &Node);
124 if (Result != TableFoundNode)
125 {
126 return NULL;
127 }
128
129 Vad = (PMMVAD_LONG)Node;
130 if (Vad->u.VadFlags.Spare == 0)
131 {
132 /* Check if this is VM VAD */
133 if (Vad->ControlArea == NULL)
134 {
135 /* We store the reactos MEMORY_AREA here */
136 MemoryArea = (PMEMORY_AREA)Vad->FirstPrototypePte;
137 }
138 else
139 {
140 /* This is a section VAD. Store the MAREA here for now */
141 MemoryArea = (PMEMORY_AREA)Vad->u4.Banked;
142 }
143 }
144 else
145 {
146 MemoryArea = (PMEMORY_AREA)Node;
147 }
148
149 ASSERT(MemoryArea != NULL);
150 return MemoryArea;
151 }
152
153 VOID
154 NTAPI
155 MiInsertVad(IN PMMVAD Vad,
156 IN PMM_AVL_TABLE VadRoot);
157
158 ULONG
159 NTAPI
160 MiMakeProtectionMask(
161 IN ULONG Protect
162 );
163
164
165 static VOID
166 MmInsertMemoryArea(
167 PMMSUPPORT AddressSpace,
168 PMEMORY_AREA marea)
169 {
170 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
171
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);
176
177 /* Build a lame VAD if this is a user-space allocation */
178 if (marea->EndingVpn + 1 < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT)
179 {
180 ASSERT(Process != NULL);
181 if (marea->Type != MEMORY_AREA_OWNED_BY_ARM3)
182 {
183 ASSERT(marea->Type == MEMORY_AREA_SECTION_VIEW || marea->Type == MEMORY_AREA_CACHE);
184
185 /* Insert the VAD */
186 MiLockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
187 MiInsertVad(&marea->VadNode, &Process->VadRoot);
188 MiUnlockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
189 marea->Vad = &marea->VadNode;
190 }
191 }
192 else
193 {
194 ASSERT(Process == NULL);
195
196 if (!MiRosKernelVadRootInitialized)
197 {
198 MiRosKernelVadRoot.BalancedRoot.u1.Parent = &MiRosKernelVadRoot.BalancedRoot;
199 MiRosKernelVadRoot.Unused = 1;
200 MiRosKernelVadRootInitialized = TRUE;
201 }
202
203 /* Insert the VAD */
204 MiLockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs);
205 MiInsertVad(&marea->VadNode, &MiRosKernelVadRoot);
206 MiUnlockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs);
207 marea->Vad = NULL;
208 }
209 }
210
211 PVOID NTAPI
212 MmFindGap(
213 PMMSUPPORT AddressSpace,
214 ULONG_PTR Length,
215 ULONG_PTR Granularity,
216 BOOLEAN TopDown)
217 {
218 PEPROCESS Process;
219 PMM_AVL_TABLE VadRoot;
220 TABLE_SEARCH_RESULT Result;
221 PMMADDRESS_NODE Parent;
222 ULONG_PTR StartingAddress, HighestAddress;
223
224 Process = MmGetAddressSpaceOwner(AddressSpace);
225 VadRoot = Process ? &Process->VadRoot : &MiRosKernelVadRoot;
226 if (TopDown)
227 {
228 /* Find an address top-down */
229 HighestAddress = Process ? (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS : (LONG_PTR)-1;
230 Result = MiFindEmptyAddressRangeDownTree(Length,
231 HighestAddress,
232 Granularity,
233 VadRoot,
234 &StartingAddress,
235 &Parent);
236 }
237 else
238 {
239 Result = MiFindEmptyAddressRangeInTree(Length,
240 Granularity,
241 VadRoot,
242 &Parent,
243 &StartingAddress);
244 }
245
246 if (Result == TableFoundNode)
247 {
248 return NULL;
249 }
250
251 return (PVOID)StartingAddress;
252 }
253
254 VOID
255 NTAPI
256 MiRemoveNode(IN PMMADDRESS_NODE Node,
257 IN PMM_AVL_TABLE Table);
258
259
260 /**
261 * @name MmFreeMemoryArea
262 *
263 * Free an existing memory area.
264 *
265 * @param AddressSpace
266 * Address space to free the area from.
267 * @param MemoryArea
268 * Memory area we're about to free.
269 * @param FreePage
270 * Callback function for each freed page.
271 * @param FreePageContext
272 * Context passed to the callback function.
273 *
274 * @return Status
275 *
276 * @remarks Lock the address space before calling this function.
277 */
278
279 NTSTATUS NTAPI
280 MmFreeMemoryArea(
281 PMMSUPPORT AddressSpace,
282 PMEMORY_AREA MemoryArea,
283 PMM_FREE_PAGE_FUNC FreePage,
284 PVOID FreePageContext)
285 {
286 ULONG_PTR Address;
287 PVOID EndAddress;
288
289 /* Make sure we own the address space lock! */
290 ASSERT(CONTAINING_RECORD(AddressSpace, EPROCESS, Vm)->AddressCreationLock.Owner == KeGetCurrentThread());
291
292 /* Check magic */
293 ASSERT(MemoryArea->Magic == 'erAM');
294
295 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
296 {
297 PEPROCESS CurrentProcess = PsGetCurrentProcess();
298 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
299
300 if (Process != NULL &&
301 Process != CurrentProcess)
302 {
303 KeAttachProcess(&Process->Pcb);
304 }
305
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)
310 {
311 BOOLEAN Dirty = FALSE;
312 SWAPENTRY SwapEntry = 0;
313 PFN_NUMBER Page = 0;
314
315 if (MmIsPageSwapEntry(Process, (PVOID)Address))
316 {
317 MmDeletePageFileMapping(Process, (PVOID)Address, &SwapEntry);
318 }
319 else
320 {
321 MmDeleteVirtualMapping(Process, (PVOID)Address, &Dirty, &Page);
322 }
323 if (FreePage != NULL)
324 {
325 FreePage(FreePageContext, MemoryArea, (PVOID)Address,
326 Page, SwapEntry, (BOOLEAN)Dirty);
327 }
328 #if (_MI_PAGING_LEVELS == 2)
329 /* Remove page table reference */
330 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
331 if ((SwapEntry || Page) && ((PVOID)Address < MmSystemRangeStart))
332 {
333 ASSERT(AddressSpace != MmGetKernelAddressSpace());
334 if (MiQueryPageTableReferences((PVOID)Address) == 0)
335 {
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);
343 }
344 }
345 #endif
346 }
347
348 if (Process != NULL &&
349 Process != CurrentProcess)
350 {
351 KeDetachProcess();
352 }
353
354 //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT
355 if (MemoryArea->Vad)
356 {
357 ASSERT(MemoryArea->EndingVpn + 1 < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT);
358 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW || MemoryArea->Type == MEMORY_AREA_CACHE);
359
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)
363 {
364 MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &Process->VadRoot);
365 }
366
367 MemoryArea->Vad = NULL;
368 }
369 else
370 {
371 MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &MiRosKernelVadRoot);
372 }
373 }
374
375 #if DBG
376 MemoryArea->Magic = 'daeD';
377 #endif
378 ExFreePoolWithTag(MemoryArea, TAG_MAREA);
379
380 DPRINT("MmFreeMemoryAreaByNode() succeeded\n");
381
382 return STATUS_SUCCESS;
383 }
384
385 /**
386 * @name MmCreateMemoryArea
387 *
388 * Create a memory area.
389 *
390 * @param AddressSpace
391 * Address space to create the area in.
392 * @param Type
393 * Type of the memory area.
394 * @param BaseAddress
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.
399 * @param Length
400 * Length of the area to allocate.
401 * @param Attributes
402 * Protection attributes for the memory area.
403 * @param Result
404 * Receives a pointer to the memory area on successful exit.
405 *
406 * @return Status
407 *
408 * @remarks Lock the address space before calling this function.
409 */
410
411 NTSTATUS NTAPI
412 MmCreateMemoryArea(PMMSUPPORT AddressSpace,
413 ULONG Type,
414 PVOID *BaseAddress,
415 ULONG_PTR Length,
416 ULONG Protect,
417 PMEMORY_AREA *Result,
418 ULONG AllocationFlags,
419 ULONG Granularity)
420 {
421 ULONG_PTR tmpLength;
422 PMEMORY_AREA MemoryArea;
423 ULONG_PTR EndingAddress;
424
425 DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
426 "*BaseAddress %p, Length %p, AllocationFlags %x, "
427 "Result %p)\n",
428 Type, BaseAddress, *BaseAddress, Length, AllocationFlags,
429 Result);
430
431 /* Is this a static memory area? */
432 if (Type & MEMORY_AREA_STATIC)
433 {
434 /* Use the static array instead of the pool */
435 ASSERT(MiStaticMemoryAreaCount < MI_STATIC_MEMORY_AREAS);
436 MemoryArea = &MiStaticMemoryAreas[MiStaticMemoryAreaCount++];
437 }
438 else
439 {
440 /* Allocate the memory area from nonpaged pool */
441 MemoryArea = ExAllocatePoolWithTag(NonPagedPool,
442 sizeof(MEMORY_AREA),
443 TAG_MAREA);
444 }
445
446 if (!MemoryArea)
447 {
448 DPRINT1("Not enough memory.\n");
449 return STATUS_NO_MEMORY;
450 }
451
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;
458
459 if (*BaseAddress == 0)
460 {
461 tmpLength = (ULONG_PTR)MM_ROUND_UP(Length, PAGE_SIZE);
462 *BaseAddress = MmFindGap(AddressSpace,
463 tmpLength,
464 Granularity,
465 (AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN);
466 if ((*BaseAddress) == 0)
467 {
468 DPRINT("No suitable gap\n");
469 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
470 return STATUS_NO_MEMORY;
471 }
472
473 MemoryArea->StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT;
474 MemoryArea->EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
475 MmInsertMemoryArea(AddressSpace, MemoryArea);
476 }
477 else
478 {
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;
482
483 if (!MmGetAddressSpaceOwner(AddressSpace) && *BaseAddress < MmSystemRangeStart)
484 {
485 ASSERT(FALSE);
486 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
487 return STATUS_ACCESS_VIOLATION;
488 }
489
490 if (MmGetAddressSpaceOwner(AddressSpace) &&
491 (ULONG_PTR)(*BaseAddress) + tmpLength > (ULONG_PTR)MmSystemRangeStart)
492 {
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;
496 }
497
498 /* No need to check ARM3 owned memory areas, the range MUST be free */
499 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
500 {
501 if (MmLocateMemoryAreaByRegion(AddressSpace,
502 *BaseAddress,
503 tmpLength) != NULL)
504 {
505 DPRINT("Memory area already occupied\n");
506 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
507 return STATUS_CONFLICTING_ADDRESSES;
508 }
509 }
510
511 MemoryArea->StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT;
512 MemoryArea->EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
513 MmInsertMemoryArea(AddressSpace, MemoryArea);
514 }
515
516 *Result = MemoryArea;
517
518 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress);
519 return STATUS_SUCCESS;
520 }
521
522 VOID
523 NTAPI
524 MiRosCleanupMemoryArea(
525 PEPROCESS Process,
526 PMMVAD Vad)
527 {
528 PMEMORY_AREA MemoryArea;
529 PVOID BaseAddress;
530 NTSTATUS Status;
531
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));
539
540 /* We are in cleanup, we don't need to synchronize */
541 MmUnlockAddressSpace(&Process->Vm);
542
543 MemoryArea = (PMEMORY_AREA)Vad;
544 BaseAddress = (PVOID)MA_GetStartingAddress(MemoryArea);
545
546 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
547 {
548 Status = MiRosUnmapViewOfSection(Process, BaseAddress, 0);
549 }
550 else if (MemoryArea->Type == MEMORY_AREA_CACHE)
551 {
552 Status = MmUnmapViewOfCacheSegment(&Process->Vm, BaseAddress);
553 }
554 else
555 {
556 /* There shouldn't be anything else! */
557 ASSERT(FALSE);
558 }
559
560 /* Make sure this worked! */
561 ASSERT(NT_SUCCESS(Status));
562
563 /* Lock the address space again */
564 MmLockAddressSpace(&Process->Vm);
565 }
566
567 VOID
568 NTAPI
569 MmDeleteProcessAddressSpace2(IN PEPROCESS Process);
570
571 NTSTATUS
572 NTAPI
573 MmDeleteProcessAddressSpace(PEPROCESS Process)
574 {
575 KIRQL OldIrql;
576 PVOID Address;
577
578 DPRINT("MmDeleteProcessAddressSpace(Process %p (%s))\n", Process,
579 Process->ImageFileName);
580
581 #ifndef _M_AMD64
582 OldIrql = MiAcquireExpansionLock();
583 RemoveEntryList(&Process->MmProcessLinks);
584 MiReleaseExpansionLock(OldIrql);
585 #endif
586 MmLockAddressSpace(&Process->Vm);
587
588 /* There should not be any memory areas left! */
589 ASSERT(Process->Vm.WorkingSetExpansionLinks.Flink == NULL);
590
591 #if (_MI_PAGING_LEVELS == 2)
592 {
593 KIRQL OldIrql;
594 PMMPDE pointerPde;
595 /* Attach to Process */
596 KeAttachProcess(&Process->Pcb);
597
598 /* Acquire PFN lock */
599 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
600
601 for (Address = MI_LOWEST_VAD_ADDRESS;
602 Address < MM_HIGHEST_VAD_ADDRESS;
603 Address =(PVOID)((ULONG_PTR)Address + (PAGE_SIZE * PTE_COUNT)))
604 {
605 /* At this point all references should be dead */
606 if (MiQueryPageTableReferences(Address) != 0)
607 {
608 DPRINT1("Process %p, Address %p, UsedPageTableEntries %lu\n",
609 Process,
610 Address,
611 MiQueryPageTableReferences(Address));
612 ASSERT(MiQueryPageTableReferences(Address) == 0);
613 }
614
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);
621 }
622
623 /* Release lock */
624 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
625
626 /* Detach */
627 KeDetachProcess();
628 }
629 #endif
630
631 MmUnlockAddressSpace(&Process->Vm);
632
633 DPRINT("Finished MmDeleteProcessAddressSpace()\n");
634 MmDeleteProcessAddressSpace2(Process);
635 return(STATUS_SUCCESS);
636 }
637
638 /* EOF */