xref: /reactos/ntoskrnl/mm/marea.c (revision ffb20d33)
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
MmLocateMemoryAreaByAddress(PMMSUPPORT AddressSpace,PVOID Address_)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
MmLocateMemoryAreaByRegion(PMMSUPPORT AddressSpace,PVOID Address_,ULONG_PTR Length)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
MmInsertMemoryArea(PMMSUPPORT AddressSpace,PMEMORY_AREA marea,ULONG Protect)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
MmFindGap(PMMSUPPORT AddressSpace,ULONG_PTR Length,ULONG_PTR Granularity,BOOLEAN TopDown)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
MmFreeMemoryArea(PMMSUPPORT AddressSpace,PMEMORY_AREA MemoryArea,PMM_FREE_PAGE_FUNC FreePage,PVOID FreePageContext)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) && (Process != CurrentProcess))
304         {
305             KeAttachProcess(&Process->Pcb);
306         }
307 
308         EndAddress = MM_ROUND_UP(MA_GetEndingAddress(MemoryArea), PAGE_SIZE);
309         for (Address = MA_GetStartingAddress(MemoryArea);
310                 Address < (ULONG_PTR)EndAddress;
311                 Address += PAGE_SIZE)
312         {
313             BOOLEAN Dirty = FALSE;
314             SWAPENTRY SwapEntry = 0;
315             PFN_NUMBER Page = 0;
316             BOOLEAN DoFree;
317 
318             if (MmIsPageSwapEntry(Process, (PVOID)Address))
319             {
320                 MmDeletePageFileMapping(Process, (PVOID)Address, &SwapEntry);
321                 /* We'll have to do some cleanup when we're on the page file */
322                 DoFree = TRUE;
323             }
324             else if (FreePage == NULL)
325             {
326                 DoFree = MmDeletePhysicalMapping(Process, (PVOID)Address, &Dirty, &Page);
327             }
328             else
329             {
330                 DoFree = MmDeleteVirtualMapping(Process, (PVOID)Address, &Dirty, &Page);
331             }
332             if (DoFree && (FreePage != NULL))
333             {
334                 FreePage(FreePageContext, MemoryArea, (PVOID)Address,
335                          Page, SwapEntry, (BOOLEAN)Dirty);
336             }
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 MmDeleteProcessAddressSpace) */
350             ASSERT(MemoryArea->VadNode.u.VadFlags.Spare != 0);
351             if (((PMMVAD)MemoryArea->Vad)->u.VadFlags.Spare == 1)
352             {
353                 MiLockProcessWorkingSet(PsGetCurrentProcess(), PsGetCurrentThread());
354                 MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &Process->VadRoot);
355                 MiUnlockProcessWorkingSet(PsGetCurrentProcess(), PsGetCurrentThread());
356             }
357 
358             MemoryArea->Vad = NULL;
359         }
360         else
361         {
362             MiLockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs);
363             MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &MiRosKernelVadRoot);
364             MiUnlockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs);
365         }
366 
367         if ((Process != NULL) && (Process != CurrentProcess))
368         {
369             KeDetachProcess();
370         }
371     }
372 
373 #if DBG
374     MemoryArea->Magic = 'daeD';
375 #endif
376     ExFreePoolWithTag(MemoryArea, TAG_MAREA);
377 
378     DPRINT("MmFreeMemoryArea() succeeded\n");
379 
380     return STATUS_SUCCESS;
381 }
382 
383 /**
384  * @name MmCreateMemoryArea
385  *
386  * Create a memory area.
387  *
388  * @param AddressSpace
389  *        Address space to create the area in.
390  * @param Type
391  *        Type of the memory area.
392  * @param BaseAddress
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.
397  * @param Length
398  *        Length of the area to allocate.
399  * @param Attributes
400  *        Protection attributes for the memory area.
401  * @param Result
402  *        Receives a pointer to the memory area on successful exit.
403  *
404  * @return Status
405  *
406  * @remarks Lock the address space before calling this function.
407  */
408 
409 NTSTATUS NTAPI
MmCreateMemoryArea(PMMSUPPORT AddressSpace,ULONG Type,PVOID * BaseAddress,ULONG_PTR Length,ULONG Protect,PMEMORY_AREA * Result,ULONG AllocationFlags,ULONG Granularity)410 MmCreateMemoryArea(PMMSUPPORT AddressSpace,
411                    ULONG Type,
412                    PVOID *BaseAddress,
413                    ULONG_PTR Length,
414                    ULONG Protect,
415                    PMEMORY_AREA *Result,
416                    ULONG AllocationFlags,
417                    ULONG Granularity)
418 {
419     ULONG_PTR tmpLength;
420     PMEMORY_AREA MemoryArea;
421     ULONG_PTR EndingAddress;
422 
423     DPRINT("MmCreateMemoryArea(Type 0x%lx, BaseAddress %p, "
424            "*BaseAddress %p, Length %p, AllocationFlags %x, "
425            "Result %p)\n",
426            Type, BaseAddress, *BaseAddress, Length, AllocationFlags,
427            Result);
428 
429     /* Is this a static memory area? */
430     if (Type & MEMORY_AREA_STATIC)
431     {
432         /* Use the static array instead of the pool */
433         ASSERT(MiStaticMemoryAreaCount < MI_STATIC_MEMORY_AREAS);
434         MemoryArea = &MiStaticMemoryAreas[MiStaticMemoryAreaCount++];
435     }
436     else
437     {
438         /* Allocate the memory area from nonpaged pool */
439         MemoryArea = ExAllocatePoolWithTag(NonPagedPool,
440                                            sizeof(MEMORY_AREA),
441                                            TAG_MAREA);
442     }
443 
444     if (!MemoryArea)
445     {
446         DPRINT1("Not enough memory.\n");
447         return STATUS_NO_MEMORY;
448     }
449 
450     RtlZeroMemory(MemoryArea, sizeof(MEMORY_AREA));
451     MemoryArea->Type = Type & ~MEMORY_AREA_STATIC;
452     MemoryArea->Flags = AllocationFlags;
453     MemoryArea->Magic = 'erAM';
454     MemoryArea->DeleteInProgress = FALSE;
455 
456     if (*BaseAddress == 0)
457     {
458         tmpLength = (ULONG_PTR)MM_ROUND_UP(Length, PAGE_SIZE);
459         *BaseAddress = MmFindGap(AddressSpace,
460                                  tmpLength,
461                                  Granularity,
462                                  (AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN);
463         if ((*BaseAddress) == 0)
464         {
465             DPRINT("No suitable gap\n");
466             if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
467             return STATUS_NO_MEMORY;
468         }
469 
470         MemoryArea->VadNode.StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT;
471         MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
472         MmInsertMemoryArea(AddressSpace, MemoryArea, Protect);
473     }
474     else
475     {
476         EndingAddress = ((ULONG_PTR)*BaseAddress + Length - 1) | (PAGE_SIZE - 1);
477         *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, Granularity);
478         tmpLength = EndingAddress + 1 - (ULONG_PTR)*BaseAddress;
479 
480         if (!MmGetAddressSpaceOwner(AddressSpace) && *BaseAddress < MmSystemRangeStart)
481         {
482             ASSERT(FALSE);
483             if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
484             return STATUS_ACCESS_VIOLATION;
485         }
486 
487         if (MmGetAddressSpaceOwner(AddressSpace) &&
488                 (ULONG_PTR)(*BaseAddress) + tmpLength > (ULONG_PTR)MmSystemRangeStart)
489         {
490             DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n");
491             if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
492             return STATUS_ACCESS_VIOLATION;
493         }
494 
495         /* No need to check ARM3 owned memory areas, the range MUST be free */
496         if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
497         {
498             if (MmLocateMemoryAreaByRegion(AddressSpace,
499                                            *BaseAddress,
500                                            tmpLength) != NULL)
501             {
502                 DPRINT("Memory area already occupied\n");
503                 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA);
504                 return STATUS_CONFLICTING_ADDRESSES;
505             }
506         }
507 
508         MemoryArea->VadNode.StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT;
509         MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT;
510         MmInsertMemoryArea(AddressSpace, MemoryArea, Protect);
511     }
512 
513     *Result = MemoryArea;
514 
515     DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress);
516     return STATUS_SUCCESS;
517 }
518 
519 VOID
520 NTAPI
MiRosCleanupMemoryArea(PEPROCESS Process,PMMVAD Vad)521 MiRosCleanupMemoryArea(
522     PEPROCESS Process,
523     PMMVAD Vad)
524 {
525     PMEMORY_AREA MemoryArea;
526     PVOID BaseAddress;
527     NTSTATUS Status;
528 
529     /* We must be called from MmCleanupAddressSpace and nowhere else!
530        Make sure things are as expected... */
531     ASSERT(Process == PsGetCurrentProcess());
532     ASSERT(Process->VmDeleted == TRUE);
533     ASSERT(((PsGetCurrentThread()->ThreadsProcess == Process) &&
534             (Process->ActiveThreads == 1)) ||
535            (Process->ActiveThreads == 0));
536 
537     MemoryArea = (PMEMORY_AREA)Vad;
538     BaseAddress = (PVOID)MA_GetStartingAddress(MemoryArea);
539 
540     if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
541     {
542         Status = MiRosUnmapViewOfSection(Process, BaseAddress, Process->ProcessExiting);
543     }
544 #ifdef NEWCC
545     else if (MemoryArea->Type == MEMORY_AREA_CACHE)
546     {
547         Status = MmUnmapViewOfCacheSegment(&Process->Vm, BaseAddress);
548     }
549 #endif
550     else
551     {
552         /* There shouldn't be anything else! */
553         ASSERT(FALSE);
554     }
555 
556     /* Make sure this worked! */
557     ASSERT(NT_SUCCESS(Status));
558 }
559 /* EOF */
560