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.u.VadFlags.Spare = 1; 173 marea->VadNode.u.VadFlags.Protection = MiMakeProtectionMask(marea->Protect); 174 175 /* Build a lame VAD if this is a user-space allocation */ 176 if (marea->VadNode.EndingVpn + 1 < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT) 177 { 178 ASSERT(Process != NULL); 179 if (marea->Type != MEMORY_AREA_OWNED_BY_ARM3) 180 { 181 ASSERT(marea->Type == MEMORY_AREA_SECTION_VIEW || marea->Type == MEMORY_AREA_CACHE); 182 183 /* Insert the VAD */ 184 MiLockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread()); 185 MiInsertVad(&marea->VadNode, &Process->VadRoot); 186 MiUnlockProcessWorkingSetUnsafe(PsGetCurrentProcess(), PsGetCurrentThread()); 187 marea->Vad = &marea->VadNode; 188 } 189 } 190 else 191 { 192 ASSERT(Process == NULL); 193 194 if (!MiRosKernelVadRootInitialized) 195 { 196 MiRosKernelVadRoot.BalancedRoot.u1.Parent = &MiRosKernelVadRoot.BalancedRoot; 197 MiRosKernelVadRoot.Unused = 1; 198 MiRosKernelVadRootInitialized = TRUE; 199 } 200 201 /* Insert the VAD */ 202 MiLockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs); 203 MiInsertVad(&marea->VadNode, &MiRosKernelVadRoot); 204 MiUnlockWorkingSet(PsGetCurrentThread(), &MmSystemCacheWs); 205 marea->Vad = NULL; 206 } 207 } 208 209 PVOID NTAPI 210 MmFindGap( 211 PMMSUPPORT AddressSpace, 212 ULONG_PTR Length, 213 ULONG_PTR Granularity, 214 BOOLEAN TopDown) 215 { 216 PEPROCESS Process; 217 PMM_AVL_TABLE VadRoot; 218 TABLE_SEARCH_RESULT Result; 219 PMMADDRESS_NODE Parent; 220 ULONG_PTR StartingAddress, HighestAddress; 221 222 Process = MmGetAddressSpaceOwner(AddressSpace); 223 VadRoot = Process ? &Process->VadRoot : &MiRosKernelVadRoot; 224 if (TopDown) 225 { 226 /* Find an address top-down */ 227 HighestAddress = Process ? (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS : (LONG_PTR)-1; 228 Result = MiFindEmptyAddressRangeDownTree(Length, 229 HighestAddress, 230 Granularity, 231 VadRoot, 232 &StartingAddress, 233 &Parent); 234 } 235 else 236 { 237 Result = MiFindEmptyAddressRangeInTree(Length, 238 Granularity, 239 VadRoot, 240 &Parent, 241 &StartingAddress); 242 } 243 244 if (Result == TableFoundNode) 245 { 246 return NULL; 247 } 248 249 return (PVOID)StartingAddress; 250 } 251 252 VOID 253 NTAPI 254 MiRemoveNode(IN PMMADDRESS_NODE Node, 255 IN PMM_AVL_TABLE Table); 256 257 258 /** 259 * @name MmFreeMemoryArea 260 * 261 * Free an existing memory area. 262 * 263 * @param AddressSpace 264 * Address space to free the area from. 265 * @param MemoryArea 266 * Memory area we're about to free. 267 * @param FreePage 268 * Callback function for each freed page. 269 * @param FreePageContext 270 * Context passed to the callback function. 271 * 272 * @return Status 273 * 274 * @remarks Lock the address space before calling this function. 275 */ 276 277 NTSTATUS NTAPI 278 MmFreeMemoryArea( 279 PMMSUPPORT AddressSpace, 280 PMEMORY_AREA MemoryArea, 281 PMM_FREE_PAGE_FUNC FreePage, 282 PVOID FreePageContext) 283 { 284 ULONG_PTR Address; 285 PVOID EndAddress; 286 287 /* Make sure we own the address space lock! */ 288 ASSERT(CONTAINING_RECORD(AddressSpace, EPROCESS, Vm)->AddressCreationLock.Owner == KeGetCurrentThread()); 289 290 /* Check magic */ 291 ASSERT(MemoryArea->Magic == 'erAM'); 292 293 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3) 294 { 295 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 296 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); 297 298 if (Process != NULL && 299 Process != CurrentProcess) 300 { 301 KeAttachProcess(&Process->Pcb); 302 } 303 304 EndAddress = MM_ROUND_UP(MA_GetEndingAddress(MemoryArea), PAGE_SIZE); 305 for (Address = MA_GetStartingAddress(MemoryArea); 306 Address < (ULONG_PTR)EndAddress; 307 Address += PAGE_SIZE) 308 { 309 BOOLEAN Dirty = FALSE; 310 SWAPENTRY SwapEntry = 0; 311 PFN_NUMBER Page = 0; 312 313 if (MmIsPageSwapEntry(Process, (PVOID)Address)) 314 { 315 MmDeletePageFileMapping(Process, (PVOID)Address, &SwapEntry); 316 } 317 else 318 { 319 MmDeleteVirtualMapping(Process, (PVOID)Address, &Dirty, &Page); 320 } 321 if (FreePage != NULL) 322 { 323 FreePage(FreePageContext, MemoryArea, (PVOID)Address, 324 Page, SwapEntry, (BOOLEAN)Dirty); 325 } 326 #if (_MI_PAGING_LEVELS == 2) 327 /* Remove page table reference */ 328 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 329 if ((SwapEntry || Page) && ((PVOID)Address < MmSystemRangeStart)) 330 { 331 ASSERT(AddressSpace != MmGetKernelAddressSpace()); 332 if (MiQueryPageTableReferences((PVOID)Address) == 0) 333 { 334 /* No PTE relies on this PDE. Release it */ 335 KIRQL OldIrql = MiAcquirePfnLock(); 336 PMMPDE PointerPde = MiAddressToPde(Address); 337 ASSERT(PointerPde->u.Hard.Valid == 1); 338 MiDeletePte(PointerPde, MiPdeToPte(PointerPde), Process, NULL); 339 ASSERT(PointerPde->u.Hard.Valid == 0); 340 MiReleasePfnLock(OldIrql); 341 } 342 } 343 #endif 344 } 345 346 if (Process != NULL && 347 Process != CurrentProcess) 348 { 349 KeDetachProcess(); 350 } 351 352 //if (MemoryArea->VadNode.StartingVpn < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT 353 if (MemoryArea->Vad) 354 { 355 ASSERT(MemoryArea->VadNode.EndingVpn + 1 < (ULONG_PTR)MmSystemRangeStart >> PAGE_SHIFT); 356 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW || MemoryArea->Type == MEMORY_AREA_CACHE); 357 358 /* MmCleanProcessAddressSpace might have removed it (and this would be MmDeleteProcessAdressSpace) */ 359 ASSERT(MemoryArea->VadNode.u.VadFlags.Spare != 0); 360 if (((PMMVAD)MemoryArea->Vad)->u.VadFlags.Spare == 1) 361 { 362 MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &Process->VadRoot); 363 } 364 365 MemoryArea->Vad = NULL; 366 } 367 else 368 { 369 MiRemoveNode((PMMADDRESS_NODE)&MemoryArea->VadNode, &MiRosKernelVadRoot); 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->Protect = Protect; 453 MemoryArea->Flags = AllocationFlags; 454 MemoryArea->Magic = 'erAM'; 455 MemoryArea->DeleteInProgress = FALSE; 456 457 if (*BaseAddress == 0) 458 { 459 tmpLength = (ULONG_PTR)MM_ROUND_UP(Length, PAGE_SIZE); 460 *BaseAddress = MmFindGap(AddressSpace, 461 tmpLength, 462 Granularity, 463 (AllocationFlags & MEM_TOP_DOWN) == MEM_TOP_DOWN); 464 if ((*BaseAddress) == 0) 465 { 466 DPRINT("No suitable gap\n"); 467 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA); 468 return STATUS_NO_MEMORY; 469 } 470 471 MemoryArea->VadNode.StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT; 472 MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT; 473 MmInsertMemoryArea(AddressSpace, MemoryArea); 474 } 475 else 476 { 477 EndingAddress = ((ULONG_PTR)*BaseAddress + Length - 1) | (PAGE_SIZE - 1); 478 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, Granularity); 479 tmpLength = EndingAddress + 1 - (ULONG_PTR)*BaseAddress; 480 481 if (!MmGetAddressSpaceOwner(AddressSpace) && *BaseAddress < MmSystemRangeStart) 482 { 483 ASSERT(FALSE); 484 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA); 485 return STATUS_ACCESS_VIOLATION; 486 } 487 488 if (MmGetAddressSpaceOwner(AddressSpace) && 489 (ULONG_PTR)(*BaseAddress) + tmpLength > (ULONG_PTR)MmSystemRangeStart) 490 { 491 DPRINT("Memory area for user mode address space exceeds MmSystemRangeStart\n"); 492 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA); 493 return STATUS_ACCESS_VIOLATION; 494 } 495 496 /* No need to check ARM3 owned memory areas, the range MUST be free */ 497 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3) 498 { 499 if (MmLocateMemoryAreaByRegion(AddressSpace, 500 *BaseAddress, 501 tmpLength) != NULL) 502 { 503 DPRINT("Memory area already occupied\n"); 504 if (!(Type & MEMORY_AREA_STATIC)) ExFreePoolWithTag(MemoryArea, TAG_MAREA); 505 return STATUS_CONFLICTING_ADDRESSES; 506 } 507 } 508 509 MemoryArea->VadNode.StartingVpn = (ULONG_PTR)*BaseAddress >> PAGE_SHIFT; 510 MemoryArea->VadNode.EndingVpn = ((ULONG_PTR)*BaseAddress + tmpLength - 1) >> PAGE_SHIFT; 511 MmInsertMemoryArea(AddressSpace, MemoryArea); 512 } 513 514 *Result = MemoryArea; 515 516 DPRINT("MmCreateMemoryArea() succeeded (%p)\n", *BaseAddress); 517 return STATUS_SUCCESS; 518 } 519 520 VOID 521 NTAPI 522 MiRosCleanupMemoryArea( 523 PEPROCESS Process, 524 PMMVAD Vad) 525 { 526 PMEMORY_AREA MemoryArea; 527 PVOID BaseAddress; 528 NTSTATUS Status; 529 530 /* We must be called from MmCleanupAddressSpace and nowhere else! 531 Make sure things are as expected... */ 532 ASSERT(Process == PsGetCurrentProcess()); 533 ASSERT(Process->VmDeleted == TRUE); 534 ASSERT(((PsGetCurrentThread()->ThreadsProcess == Process) && 535 (Process->ActiveThreads == 1)) || 536 (Process->ActiveThreads == 0)); 537 538 /* We are in cleanup, we don't need to synchronize */ 539 MmUnlockAddressSpace(&Process->Vm); 540 541 MemoryArea = (PMEMORY_AREA)Vad; 542 BaseAddress = (PVOID)MA_GetStartingAddress(MemoryArea); 543 544 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 545 { 546 Status = MiRosUnmapViewOfSection(Process, BaseAddress, Process->ProcessExiting); 547 } 548 else if (MemoryArea->Type == MEMORY_AREA_CACHE) 549 { 550 Status = MmUnmapViewOfCacheSegment(&Process->Vm, BaseAddress); 551 } 552 else 553 { 554 /* There shouldn't be anything else! */ 555 ASSERT(FALSE); 556 } 557 558 /* Make sure this worked! */ 559 ASSERT(NT_SUCCESS(Status)); 560 561 /* Lock the address space again */ 562 MmLockAddressSpace(&Process->Vm); 563 } 564 565 VOID 566 NTAPI 567 MmDeleteProcessAddressSpace2(IN PEPROCESS Process); 568 569 NTSTATUS 570 NTAPI 571 MmDeleteProcessAddressSpace(PEPROCESS Process) 572 { 573 #ifndef _M_AMD64 574 KIRQL OldIrql; 575 #endif 576 577 DPRINT("MmDeleteProcessAddressSpace(Process %p (%s))\n", Process, 578 Process->ImageFileName); 579 580 #ifndef _M_AMD64 581 OldIrql = MiAcquireExpansionLock(); 582 RemoveEntryList(&Process->MmProcessLinks); 583 MiReleaseExpansionLock(OldIrql); 584 #endif 585 MmLockAddressSpace(&Process->Vm); 586 587 /* There should not be any memory areas left! */ 588 ASSERT(Process->Vm.WorkingSetExpansionLinks.Flink == NULL); 589 590 #if (_MI_PAGING_LEVELS == 2) 591 { 592 KIRQL OldIrql; 593 PVOID Address; 594 PMMPDE pointerPde; 595 596 /* Attach to Process */ 597 KeAttachProcess(&Process->Pcb); 598 599 /* Acquire PFN lock */ 600 OldIrql = MiAcquirePfnLock(); 601 602 for (Address = MI_LOWEST_VAD_ADDRESS; 603 Address < MM_HIGHEST_VAD_ADDRESS; 604 Address = (PVOID)((ULONG_PTR)Address + (PTE_PER_PAGE * PAGE_SIZE))) 605 { 606 /* At this point all references should be dead */ 607 if (MiQueryPageTableReferences(Address) != 0) 608 { 609 DPRINT1("Process %p, Address %p, UsedPageTableEntries %lu\n", 610 Process, 611 Address, 612 MiQueryPageTableReferences(Address)); 613 ASSERT(MiQueryPageTableReferences(Address) == 0); 614 } 615 616 pointerPde = MiAddressToPde(Address); 617 /* Unlike in ARM3, we don't necesarrily free the PDE page as soon as reference reaches 0, 618 * so we must clean up a bit when process closes */ 619 if (pointerPde->u.Hard.Valid) 620 MiDeletePte(pointerPde, MiPdeToPte(pointerPde), Process, NULL); 621 ASSERT(pointerPde->u.Hard.Valid == 0); 622 } 623 624 /* Release lock */ 625 MiReleasePfnLock(OldIrql); 626 627 /* Detach */ 628 KeDetachProcess(); 629 } 630 #endif 631 632 MmUnlockAddressSpace(&Process->Vm); 633 634 DPRINT("Finished MmDeleteProcessAddressSpace()\n"); 635 MmDeleteProcessAddressSpace2(Process); 636 return(STATUS_SUCCESS); 637 } 638 639 /* EOF */ 640