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) && (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 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 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