xref: /reactos/ntoskrnl/mm/marea.c (revision 6924b8ff)
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     ULONG Protect)
170 {
171     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
172 
173     marea->VadNode.u.VadFlags.Spare = 1;
174     marea->VadNode.u.VadFlags.Protection = MiMakeProtectionMask(Protect);
175 
176     /* Build a lame VAD if this is a user-space allocation */
177     if (marea->VadNode.EndingVpn + 1 < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT)
178     {
179         ASSERT(Process != NULL);
180         if (marea->Type != MEMORY_AREA_OWNED_BY_ARM3)
181         {
182 #ifdef NEWCC
183             ASSERT(marea->Type == MEMORY_AREA_SECTION_VIEW || marea->Type == MEMORY_AREA_CACHE);
184 #else
185             ASSERT(marea->Type == MEMORY_AREA_SECTION_VIEW);
186 #endif
187 
188             /* Insert the VAD */
189             MiLockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
190             MiInsertVad(&marea->VadNode, &Process->VadRoot);
191             MiUnlockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread());
192             marea->Vad = &marea->VadNode;
193         }
194     }
195     else
196     {
197         ASSERT(Process == NULL);
198 
199         if (!MiRosKernelVadRootInitialized)
200         {
201             MiRosKernelVadRoot.BalancedRoot.u1.Parent = &MiRosKernelVadRoot.BalancedRoot;
202             MiRosKernelVadRoot.Unused = 1;
203             MiRosKernelVadRootInitialized = TRUE;
204         }
205 
206         /* Insert the VAD */
207         MiLockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs);
208         MiInsertVad(&marea->VadNode, &MiRosKernelVadRoot);
209         MiUnlockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs);
210         marea->Vad = NULL;
211     }
212 }
213 
214 PVOID NTAPI
215 MmFindGap(
216     PMMSUPPORT AddressSpace,
217     ULONG_PTR Length,
218     ULONG_PTR Granularity,
219     BOOLEAN TopDown)
220 {
221     PEPROCESS Process;
222     PMM_AVL_TABLE VadRoot;
223     TABLE_SEARCH_RESULT Result;
224     PMMADDRESS_NODE Parent;
225     ULONG_PTR StartingAddress, HighestAddress;
226 
227     Process = MmGetAddressSpaceOwner(AddressSpace);
228     VadRoot = Process ? &Process->VadRoot : &MiRosKernelVadRoot;
229     if (TopDown)
230     {
231         /* Find an address top-down */
232         HighestAddress = Process ? (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS : (LONG_PTR)-1;
233         Result = MiFindEmptyAddressRangeDownTree(Length,
234                                                  HighestAddress,
235                                                  Granularity,
236                                                  VadRoot,
237                                                  &StartingAddress,
238                                                  &Parent);
239     }
240     else
241     {
242         Result = MiFindEmptyAddressRangeInTree(Length,
243                                                Granularity,
244                                                VadRoot,
245                                                &Parent,
246                                                &StartingAddress);
247     }
248 
249     if (Result == TableFoundNode)
250     {
251         return NULL;
252     }
253 
254     return (PVOID)StartingAddress;
255 }
256 
257 VOID
258 NTAPI
259 MiRemoveNode(IN PMMADDRESS_NODE Node,
260              IN PMM_AVL_TABLE Table);
261 
262 
263 /**
264  * @name MmFreeMemoryArea
265  *
266  * Free an existing memory area.
267  *
268  * @param AddressSpace
269  *        Address space to free the area from.
270  * @param MemoryArea
271  *        Memory area we're about to free.
272  * @param FreePage
273  *        Callback function for each freed page.
274  * @param FreePageContext
275  *        Context passed to the callback function.
276  *
277  * @return Status
278  *
279  * @remarks Lock the address space before calling this function.
280  */
281 
282 NTSTATUS NTAPI
283 MmFreeMemoryArea(
284     PMMSUPPORT AddressSpace,
285     PMEMORY_AREA MemoryArea,
286     PMM_FREE_PAGE_FUNC FreePage,
287     PVOID FreePageContext)
288 {
289     ULONG_PTR Address;
290     PVOID EndAddress;
291 
292     /* Make sure we own the address space lock! */
293     ASSERT(CONTAINING_RECORD(AddressSpace, EPROCESS, Vm)->AddressCreationLock.Owner == KeGetCurrentThread());
294 
295     /* Check magic */
296     ASSERT(MemoryArea->Magic == 'erAM');
297 
298     if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
299     {
300         PEPROCESS CurrentProcess = PsGetCurrentProcess();
301         PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
302 
303         if (Process != NULL &&
304                 Process != CurrentProcess)
305         {
306             KeAttachProcess(&Process->Pcb);
307         }
308 
309         EndAddress = MM_ROUND_UP(MA_GetEndingAddress(MemoryArea), PAGE_SIZE);
310         for (Address = MA_GetStartingAddress(MemoryArea);
311                 Address < (ULONG_PTR)EndAddress;
312                 Address += PAGE_SIZE)
313         {
314             BOOLEAN Dirty = FALSE;
315             SWAPENTRY SwapEntry = 0;
316             PFN_NUMBER Page = 0;
317 
318             if (MmIsPageSwapEntry(Process, (PVOID)Address))
319             {
320                 MmDeletePageFileMapping(Process, (PVOID)Address, &SwapEntry);
321             }
322             else
323             {
324                 MmDeleteVirtualMapping(Process, (PVOID)Address, &Dirty, &Page);
325             }
326             if (FreePage != NULL)
327             {
328                 FreePage(FreePageContext, MemoryArea, (PVOID)Address,
329                          Page, SwapEntry, (BOOLEAN)Dirty);
330             }
331         }
332 
333         if (Process != NULL &&
334                 Process != CurrentProcess)
335         {
336             KeDetachProcess();
337         }
338 
339         //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT
340         if (MemoryArea->Vad)
341         {
342             ASSERT(MemoryArea->VadNode.EndingVpn + 1 < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT);
343 #ifdef NEWCC
344             ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW || MemoryArea->Type == MEMORY_AREA_CACHE);
345 #else
346             ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
347 #endif
348 
349             /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */
350             ASSERT(MemoryArea->VadNode.u.VadFlags.Spare != 0);
351             if (((PMMVAD)MemoryArea->Vad)->u.VadFlags.Spare == 1)
352             {
353                 MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &Process->VadRoot);
354             }
355 
356             MemoryArea->Vad = NULL;
357         }
358         else
359         {
360             MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &MiRosKernelVadRoot);
361         }
362     }
363 
364 #if DBG
365     MemoryArea->Magic = 'daeD';
366 #endif
367     ExFreePoolWithTag(MemoryArea, TAG_MAREA);
368 
369     DPRINT("MmFreeMemoryArea() succeeded\n");
370 
371     return STATUS_SUCCESS;
372 }
373 
374 /**
375  * @name MmCreateMemoryArea
376  *
377  * Create a memory area.
378  *
379  * @param AddressSpace
380  *        Address space to create the area in.
381  * @param Type
382  *        Type of the memory area.
383  * @param BaseAddress
384  *        Base address for the memory area we're about the create. On
385  *        input it contains either 0 (auto-assign address) or preferred
386  *        address. On output it contains the starting address of the
387  *        newly created area.
388  * @param Length
389  *        Length of the area to allocate.
390  * @param Attributes
391  *        Protection attributes for the memory area.
392  * @param Result
393  *        Receives a pointer to the memory area on successful exit.
394  *
395  * @return Status
396  *
397  * @remarks Lock the address space before calling this function.
398  */
399 
400 NTSTATUS NTAPI
401 MmCreateMemoryArea(PMMSUPPORT AddressSpace,
402                    ULONG Type,
403                    PVOID *BaseAddress,
404                    ULONG_PTR Length,
405                    ULONG Protect,
406                    PMEMORY_AREA *Result,
407                    ULONG AllocationFlags,
408                    ULONG Granularity)
409 {
410     ULONG_PTR tmpLength;
411     PMEMORY_AREA MemoryArea;
412     ULONG_PTR EndingAddress;
413 
414     DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
415            "*BaseAddress %p, Length %p, AllocationFlags %x, "
416            "Result %p)\n",
417            Type, BaseAddress, *BaseAddress, Length, AllocationFlags,
418            Result);
419 
420     /* Is this a static memory area? */
421     if (Type & MEMORY_AREA_STATIC)
422     {
423         /* Use the static array instead of the pool */
424         ASSERT(MiStaticMemoryAreaCount < MI_STATIC_MEMORY_AREAS);
425         MemoryArea = &MiStaticMemoryAreas[MiStaticMemoryAreaCount++];
426     }
427     else
428     {
429         /* Allocate the memory area from nonpaged pool */
430         MemoryArea = ExAllocatePoolWithTag(NonPagedPool,
431                                            sizeof(MEMORY_AREA),
432                                            TAG_MAREA);
433     }
434 
435     if (!MemoryArea)
436     {
437         DPRINT1("Not enough memory.\n");
438         return STATUS_NO_MEMORY;
439     }
440 
441     RtlZeroMemory(MemoryArea, sizeof(MEMORY_AREA));
442     MemoryArea->Type = Type & ~MEMORY_AREA_STATIC;
443     MemoryArea->Flags = AllocationFlags;
444     MemoryArea->Magic = 'erAM';
445     MemoryArea->DeleteInProgress = FALSE;
446 
447     if (*BaseAddress == 0)
448     {
449         tmpLength = (ULONG_PTR)MM_ROUND_UP(Length, PAGE_SIZE);
450         *BaseAddress = MmFindGap(AddressSpace,
451                                  tmpLength,
452                                  Granularity,
453                                  (AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN);
454         if ((*BaseAddress) == 0)
455         {
456             DPRINT("No suitable gap\n");
457             if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
458             return STATUS_NO_MEMORY;
459         }
460 
461         MemoryArea->VadNode.StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT;
462         MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
463         MmInsertMemoryArea(AddressSpace, MemoryArea, Protect);
464     }
465     else
466     {
467         EndingAddress = ((ULONG_PTR)*BaseAddress + Length - 1) | (PAGE_SIZE - 1);
468         *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, Granularity);
469         tmpLength = EndingAddress + 1 - (ULONG_PTR)*BaseAddress;
470 
471         if (!MmGetAddressSpaceOwner(AddressSpace) && *BaseAddress < MmSystemRangeStart)
472         {
473             ASSERT(FALSE);
474             if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
475             return STATUS_ACCESS_VIOLATION;
476         }
477 
478         if (MmGetAddressSpaceOwner(AddressSpace) &&
479                 (ULONG_PTR)(*BaseAddress) + tmpLength > (ULONG_PTR)MmSystemRangeStart)
480         {
481             DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
482             if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
483             return STATUS_ACCESS_VIOLATION;
484         }
485 
486         /* No need to check ARM3 owned memory areas, the range MUST be free */
487         if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
488         {
489             if (MmLocateMemoryAreaByRegion(AddressSpace,
490                                            *BaseAddress,
491                                            tmpLength) != NULL)
492             {
493                 DPRINT("Memory area already occupied\n");
494                 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
495                 return STATUS_CONFLICTING_ADDRESSES;
496             }
497         }
498 
499         MemoryArea->VadNode.StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT;
500         MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
501         MmInsertMemoryArea(AddressSpace, MemoryArea, Protect);
502     }
503 
504     *Result = MemoryArea;
505 
506     DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress);
507     return STATUS_SUCCESS;
508 }
509 
510 VOID
511 NTAPI
512 MiRosCleanupMemoryArea(
513     PEPROCESS Process,
514     PMMVAD Vad)
515 {
516     PMEMORY_AREA MemoryArea;
517     PVOID BaseAddress;
518     NTSTATUS Status;
519 
520     /* We must be called from MmCleanupAddressSpace and nowhere else!
521        Make sure things are as expected... */
522     ASSERT(Process == PsGetCurrentProcess());
523     ASSERT(Process->VmDeleted == TRUE);
524     ASSERT(((PsGetCurrentThread()->ThreadsProcess == Process) &&
525             (Process->ActiveThreads == 1)) ||
526            (Process->ActiveThreads == 0));
527 
528     MemoryArea = (PMEMORY_AREA)Vad;
529     BaseAddress = (PVOID)MA_GetStartingAddress(MemoryArea);
530 
531     if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
532     {
533         Status = MiRosUnmapViewOfSection(Process, BaseAddress, Process->ProcessExiting);
534     }
535 #ifdef NEWCC
536     else if (MemoryArea->Type == MEMORY_AREA_CACHE)
537     {
538         Status = MmUnmapViewOfCacheSegment(&Process->Vm, BaseAddress);
539     }
540 #endif
541     else
542     {
543         /* There shouldn't be anything else! */
544         ASSERT(FALSE);
545     }
546 
547     /* Make sure this worked! */
548     ASSERT(NT_SUCCESS(Status));
549 }
550 /* EOF */
551