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