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