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