1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: ntoskrnl/mm/ARM3/virtual.c 5 * PURPOSE: ARM Memory Manager Virtual Memory Management 6 * PROGRAMMERS: ReactOS Portable Systems Group 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 #define MODULE_INVOLVED_IN_ARM3 16 #include <mm/ARM3/miarm.h> 17 18 #define MI_MAPPED_COPY_PAGES 14 19 #define MI_POOL_COPY_BYTES 512 20 #define MI_MAX_TRANSFER_SIZE 64 * 1024 21 22 NTSTATUS NTAPI 23 MiProtectVirtualMemory(IN PEPROCESS Process, 24 IN OUT PVOID *BaseAddress, 25 IN OUT PSIZE_T NumberOfBytesToProtect, 26 IN ULONG NewAccessProtection, 27 OUT PULONG OldAccessProtection OPTIONAL); 28 29 VOID 30 NTAPI 31 MiFlushTbAndCapture(IN PMMVAD FoundVad, 32 IN PMMPTE PointerPte, 33 IN ULONG ProtectionMask, 34 IN PMMPFN Pfn1, 35 IN BOOLEAN CaptureDirtyBit); 36 37 38 /* PRIVATE FUNCTIONS **********************************************************/ 39 40 ULONG 41 NTAPI 42 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress, 43 IN ULONG_PTR EndingAddress, 44 IN PMMVAD Vad, 45 IN PEPROCESS Process) 46 { 47 PMMPTE PointerPte, LastPte; 48 PMMPDE PointerPde; 49 BOOLEAN OnPdeBoundary = TRUE; 50 #if _MI_PAGING_LEVELS >= 3 51 PMMPPE PointerPpe; 52 BOOLEAN OnPpeBoundary = TRUE; 53 #if _MI_PAGING_LEVELS == 4 54 PMMPXE PointerPxe; 55 BOOLEAN OnPxeBoundary = TRUE; 56 #endif 57 #endif 58 59 /* Make sure this all makes sense */ 60 ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive || PsGetCurrentThread()->OwnsProcessWorkingSetShared); 61 ASSERT(EndingAddress >= StartingAddress); 62 PointerPte = MiAddressToPte(StartingAddress); 63 LastPte = MiAddressToPte(EndingAddress); 64 65 /* 66 * In case this is a committed VAD, assume the whole range is committed 67 * and count the individually decommitted pages. 68 * In case it is not, assume the range is not committed and count the individually committed pages. 69 */ 70 ULONG_PTR CommittedPages = Vad->u.VadFlags.MemCommit ? BYTES_TO_PAGES(EndingAddress - StartingAddress) : 0; 71 72 while (PointerPte <= LastPte) 73 { 74 #if _MI_PAGING_LEVELS == 4 75 /* Check if PXE was ever paged in. */ 76 if (OnPxeBoundary) 77 { 78 PointerPxe = MiPteToPxe(PointerPte); 79 80 /* Check that this loop is sane */ 81 ASSERT(OnPpeBoundary); 82 ASSERT(OnPdeBoundary); 83 84 if (PointerPxe->u.Long == 0) 85 { 86 PointerPxe++; 87 PointerPte = MiPxeToPte(PointerPde); 88 continue; 89 } 90 91 if (PointerPxe->u.Hard.Valid == 0) 92 MiMakeSystemAddressValid(MiPteToPpe(PointerPte), Process); 93 } 94 ASSERT(PointerPxe->u.Hard.Valid == 1); 95 #endif 96 97 #if _MI_PAGING_LEVELS >= 3 98 /* Now PPE */ 99 if (OnPpeBoundary) 100 { 101 PointerPpe = MiPteToPpe(PointerPte); 102 103 /* Sanity again */ 104 ASSERT(OnPdeBoundary); 105 106 if (PointerPpe->u.Long == 0) 107 { 108 PointerPpe++; 109 PointerPte = MiPpeToPte(PointerPpe); 110 #if _MI_PAGING_LEVELS == 4 111 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte); 112 #endif 113 continue; 114 } 115 116 if (PointerPpe->u.Hard.Valid == 0) 117 MiMakeSystemAddressValid(MiPteToPde(PointerPte), Process); 118 } 119 ASSERT(PointerPpe->u.Hard.Valid == 1); 120 #endif 121 122 /* Last level is the PDE */ 123 if (OnPdeBoundary) 124 { 125 PointerPde = MiPteToPde(PointerPte); 126 if (PointerPde->u.Long == 0) 127 { 128 PointerPde++; 129 PointerPte = MiPdeToPte(PointerPde); 130 #if _MI_PAGING_LEVELS >= 3 131 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte); 132 #if _MI_PAGING_LEVELS == 4 133 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte); 134 #endif 135 #endif 136 continue; 137 } 138 139 if (PointerPde->u.Hard.Valid == 0) 140 MiMakeSystemAddressValid(PointerPte, Process); 141 } 142 ASSERT(PointerPde->u.Hard.Valid == 1); 143 144 /* Is this PTE demand zero? */ 145 if (PointerPte->u.Long != 0) 146 { 147 /* It isn't -- is it a decommited, invalid, or faulted PTE? */ 148 if ((PointerPte->u.Hard.Valid == 0) && 149 (PointerPte->u.Soft.Protection == MM_DECOMMIT) && 150 ((PointerPte->u.Soft.Prototype == 0) || 151 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 152 { 153 /* It is, so remove it from the count of committed pages if we have to */ 154 if (Vad->u.VadFlags.MemCommit) 155 CommittedPages--; 156 } 157 else if (!Vad->u.VadFlags.MemCommit) 158 { 159 /* It is a valid, non-decommited, non-paged out PTE. Count it in. */ 160 CommittedPages++; 161 } 162 } 163 164 /* Move to the next PTE */ 165 PointerPte++; 166 /* Manage page tables */ 167 OnPdeBoundary = MiIsPteOnPdeBoundary(PointerPte); 168 #if _MI_PAGING_LEVELS >= 3 169 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte); 170 #if _MI_PAGING_LEVELS == 4 171 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte); 172 #endif 173 #endif 174 } 175 176 /* Make sure we didn't mess this up */ 177 ASSERT(CommittedPages <= BYTES_TO_PAGES(EndingAddress - StartingAddress)); 178 return CommittedPages; 179 } 180 181 ULONG 182 NTAPI 183 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress, 184 IN PEPROCESS CurrentProcess) 185 { 186 NTSTATUS Status; 187 BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE; 188 PETHREAD CurrentThread = PsGetCurrentThread(); 189 190 /* Must be a non-pool page table, since those are double-mapped already */ 191 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS); 192 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) || 193 (PageTableVirtualAddress > MmPagedPoolEnd)); 194 195 /* Working set lock or PFN lock should be held */ 196 ASSERT(KeAreAllApcsDisabled() == TRUE); 197 198 /* Check if the page table is valid */ 199 while (!MmIsAddressValid(PageTableVirtualAddress)) 200 { 201 /* Release the working set lock */ 202 MiUnlockProcessWorkingSetForFault(CurrentProcess, 203 CurrentThread, 204 &WsSafe, 205 &WsShared); 206 207 /* Fault it in */ 208 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL); 209 if (!NT_SUCCESS(Status)) 210 { 211 /* This should not fail */ 212 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, 213 1, 214 Status, 215 (ULONG_PTR)CurrentProcess, 216 (ULONG_PTR)PageTableVirtualAddress); 217 } 218 219 /* Lock the working set again */ 220 MiLockProcessWorkingSetForFault(CurrentProcess, 221 CurrentThread, 222 WsSafe, 223 WsShared); 224 225 /* This flag will be useful later when we do better locking */ 226 LockChange = TRUE; 227 } 228 229 /* Let caller know what the lock state is */ 230 return LockChange; 231 } 232 233 ULONG 234 NTAPI 235 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress, 236 IN KIRQL OldIrql) 237 { 238 NTSTATUS Status; 239 BOOLEAN LockChange = FALSE; 240 241 /* Must be e kernel address */ 242 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS); 243 244 /* Check if the page is valid */ 245 while (!MmIsAddressValid(VirtualAddress)) 246 { 247 /* Release the PFN database */ 248 MiReleasePfnLock(OldIrql); 249 250 /* Fault it in */ 251 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL); 252 if (!NT_SUCCESS(Status)) 253 { 254 /* This should not fail */ 255 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, 256 3, 257 Status, 258 0, 259 (ULONG_PTR)VirtualAddress); 260 } 261 262 /* This flag will be useful later when we do better locking */ 263 LockChange = TRUE; 264 265 /* Lock the PFN database */ 266 OldIrql = MiAcquirePfnLock(); 267 } 268 269 /* Let caller know what the lock state is */ 270 return LockChange; 271 } 272 273 PFN_COUNT 274 NTAPI 275 MiDeleteSystemPageableVm(IN PMMPTE PointerPte, 276 IN PFN_NUMBER PageCount, 277 IN ULONG Flags, 278 OUT PPFN_NUMBER ValidPages) 279 { 280 PFN_COUNT ActualPages = 0; 281 PETHREAD CurrentThread = PsGetCurrentThread(); 282 PMMPFN Pfn1, Pfn2; 283 PFN_NUMBER PageFrameIndex, PageTableIndex; 284 KIRQL OldIrql; 285 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 286 287 /* Lock the system working set */ 288 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs); 289 290 /* Loop all pages */ 291 while (PageCount) 292 { 293 /* Make sure there's some data about the page */ 294 if (PointerPte->u.Long) 295 { 296 /* Normally this is one possibility -- freeing a valid page */ 297 if (PointerPte->u.Hard.Valid) 298 { 299 /* Get the page PFN */ 300 PageFrameIndex = PFN_FROM_PTE(PointerPte); 301 Pfn1 = MiGetPfnEntry(PageFrameIndex); 302 303 /* Should not have any working set data yet */ 304 ASSERT(Pfn1->u1.WsIndex == 0); 305 306 /* Actual valid, legitimate, pages */ 307 if (ValidPages) (*ValidPages)++; 308 309 /* Get the page table entry */ 310 PageTableIndex = Pfn1->u4.PteFrame; 311 Pfn2 = MiGetPfnEntry(PageTableIndex); 312 313 /* Lock the PFN database */ 314 OldIrql = MiAcquirePfnLock(); 315 316 /* Delete it the page */ 317 MI_SET_PFN_DELETED(Pfn1); 318 MiDecrementShareCount(Pfn1, PageFrameIndex); 319 320 /* Decrement the page table too */ 321 MiDecrementShareCount(Pfn2, PageTableIndex); 322 323 /* Release the PFN database */ 324 MiReleasePfnLock(OldIrql); 325 326 /* Destroy the PTE */ 327 MI_ERASE_PTE(PointerPte); 328 } 329 else 330 { 331 /* As always, only handle current ARM3 scenarios */ 332 ASSERT(PointerPte->u.Soft.Prototype == 0); 333 ASSERT(PointerPte->u.Soft.Transition == 0); 334 335 /* 336 * The only other ARM3 possibility is a demand zero page, which would 337 * mean freeing some of the paged pool pages that haven't even been 338 * touched yet, as part of a larger allocation. 339 * 340 * Right now, we shouldn't expect any page file information in the PTE 341 */ 342 ASSERT(PointerPte->u.Soft.PageFileHigh == 0); 343 344 /* Destroy the PTE */ 345 MI_ERASE_PTE(PointerPte); 346 } 347 348 /* Actual legitimate pages */ 349 ActualPages++; 350 } 351 352 /* Keep going */ 353 PointerPte++; 354 PageCount--; 355 } 356 357 /* Release the working set */ 358 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs); 359 360 /* Flush the entire TLB */ 361 KeFlushEntireTb(TRUE, TRUE); 362 363 /* Done */ 364 return ActualPages; 365 } 366 367 VOID 368 NTAPI 369 MiDeletePte(IN PMMPTE PointerPte, 370 IN PVOID VirtualAddress, 371 IN PEPROCESS CurrentProcess, 372 IN PMMPTE PrototypePte) 373 { 374 PMMPFN Pfn1; 375 MMPTE TempPte; 376 PFN_NUMBER PageFrameIndex; 377 PMMPDE PointerPde; 378 379 /* PFN lock must be held */ 380 MI_ASSERT_PFN_LOCK_HELD(); 381 382 /* WorkingSet must be exclusively locked */ 383 ASSERT(MM_ANY_WS_LOCK_HELD_EXCLUSIVE(PsGetCurrentThread())); 384 385 /* This must be current process. */ 386 ASSERT(CurrentProcess == PsGetCurrentProcess()); 387 388 /* Capture the PTE */ 389 TempPte = *PointerPte; 390 391 /* See if the PTE is valid */ 392 if (TempPte.u.Hard.Valid == 0) 393 { 394 /* Prototype and paged out PTEs not supported yet */ 395 ASSERT(TempPte.u.Soft.Prototype == 0); 396 ASSERT((TempPte.u.Soft.PageFileHigh == 0) || (TempPte.u.Soft.Transition == 1)); 397 398 if (TempPte.u.Soft.Transition) 399 { 400 /* Get the PFN entry */ 401 PageFrameIndex = PFN_FROM_PTE(&TempPte); 402 Pfn1 = MiGetPfnEntry(PageFrameIndex); 403 404 DPRINT("Pte %p is transitional!\n", PointerPte); 405 406 /* Make sure the saved PTE address is valid */ 407 ASSERT((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) == PointerPte); 408 409 /* Destroy the PTE */ 410 MI_ERASE_PTE(PointerPte); 411 412 /* Drop the reference on the page table. */ 413 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); 414 415 /* In case of shared page, the prototype PTE must be in transition, not the process one */ 416 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 417 418 /* Delete the PFN */ 419 MI_SET_PFN_DELETED(Pfn1); 420 421 /* It must be either free (refcount == 0) or being written (refcount == 1) */ 422 ASSERT(Pfn1->u3.e2.ReferenceCount == Pfn1->u3.e1.WriteInProgress); 423 424 /* See if we must free it ourselves, or if it will be freed once I/O is over */ 425 if (Pfn1->u3.e2.ReferenceCount == 0) 426 { 427 /* And it should be in standby or modified list */ 428 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList)); 429 430 /* Unlink it and set its reference count to one */ 431 MiUnlinkPageFromList(Pfn1); 432 Pfn1->u3.e2.ReferenceCount++; 433 434 /* This will put it back in free list and clean properly up */ 435 MiDecrementReferenceCount(Pfn1, PageFrameIndex); 436 } 437 return; 438 } 439 } 440 441 /* Get the PFN entry */ 442 PageFrameIndex = PFN_FROM_PTE(&TempPte); 443 Pfn1 = MiGetPfnEntry(PageFrameIndex); 444 445 /* Check if this is a valid, prototype PTE */ 446 if (Pfn1->u3.e1.PrototypePte == 1) 447 { 448 /* Get the PDE and make sure it's faulted in */ 449 PointerPde = MiPteToPde(PointerPte); 450 if (PointerPde->u.Hard.Valid == 0) 451 { 452 #if (_MI_PAGING_LEVELS == 2) 453 /* Could be paged pool access from a new process -- synchronize the page directories */ 454 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress))) 455 { 456 #endif 457 /* The PDE must be valid at this point */ 458 KeBugCheckEx(MEMORY_MANAGEMENT, 459 0x61940, 460 (ULONG_PTR)PointerPte, 461 PointerPte->u.Long, 462 (ULONG_PTR)VirtualAddress); 463 } 464 #if (_MI_PAGING_LEVELS == 2) 465 } 466 #endif 467 /* Drop the share count on the page table */ 468 PointerPde = MiPteToPde(PointerPte); 469 MiDecrementShareCount(MiGetPfnEntry(PointerPde->u.Hard.PageFrameNumber), 470 PointerPde->u.Hard.PageFrameNumber); 471 472 /* Drop the share count */ 473 MiDecrementShareCount(Pfn1, PageFrameIndex); 474 475 /* Either a fork, or this is the shared user data page */ 476 if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress)) 477 { 478 /* If it's not the shared user page, then crash, since there's no fork() yet */ 479 if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) || 480 (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA)) 481 { 482 /* Must be some sort of memory corruption */ 483 KeBugCheckEx(MEMORY_MANAGEMENT, 484 0x400, 485 (ULONG_PTR)PointerPte, 486 (ULONG_PTR)PrototypePte, 487 (ULONG_PTR)Pfn1->PteAddress); 488 } 489 } 490 491 /* Erase it */ 492 MI_ERASE_PTE(PointerPte); 493 } 494 else 495 { 496 /* Make sure the saved PTE address is valid */ 497 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) 498 { 499 /* The PFN entry is illegal, or invalid */ 500 KeBugCheckEx(MEMORY_MANAGEMENT, 501 0x401, 502 (ULONG_PTR)PointerPte, 503 PointerPte->u.Long, 504 (ULONG_PTR)Pfn1->PteAddress); 505 } 506 507 /* Erase the PTE */ 508 MI_ERASE_PTE(PointerPte); 509 510 /* There should only be 1 shared reference count */ 511 ASSERT(Pfn1->u2.ShareCount == 1); 512 513 /* Drop the reference on the page table. */ 514 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); 515 516 /* Mark the PFN for deletion and dereference what should be the last ref */ 517 MI_SET_PFN_DELETED(Pfn1); 518 MiDecrementShareCount(Pfn1, PageFrameIndex); 519 520 /* We should eventually do this */ 521 //CurrentProcess->NumberOfPrivatePages--; 522 } 523 524 /* Flush the TLB */ 525 KeFlushCurrentTb(); 526 } 527 528 VOID 529 NTAPI 530 MiDeleteVirtualAddresses(IN ULONG_PTR Va, 531 IN ULONG_PTR EndingAddress, 532 IN PMMVAD Vad) 533 { 534 PMMPTE PointerPte, PrototypePte, LastPrototypePte; 535 PMMPDE PointerPde; 536 #if (_MI_PAGING_LEVELS >= 3) 537 PMMPPE PointerPpe; 538 #endif 539 #if (_MI_PAGING_LEVELS >= 4) 540 PMMPPE PointerPxe; 541 #endif 542 MMPTE TempPte; 543 PEPROCESS CurrentProcess; 544 KIRQL OldIrql; 545 BOOLEAN AddressGap = FALSE; 546 PSUBSECTION Subsection; 547 548 /* Get out if this is a fake VAD, RosMm will free the marea pages */ 549 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return; 550 551 /* Get the current process */ 552 CurrentProcess = PsGetCurrentProcess(); 553 554 /* Check if this is a section VAD or a VM VAD */ 555 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte)) 556 { 557 /* Don't worry about prototypes */ 558 PrototypePte = LastPrototypePte = NULL; 559 } 560 else 561 { 562 /* Get the prototype PTE */ 563 PrototypePte = Vad->FirstPrototypePte; 564 LastPrototypePte = Vad->FirstPrototypePte + 1; 565 } 566 567 /* In all cases, we don't support fork() yet */ 568 ASSERT(CurrentProcess->CloneRoot == NULL); 569 570 /* Loop the PTE for each VA (EndingAddress is inclusive!) */ 571 while (Va <= EndingAddress) 572 { 573 #if (_MI_PAGING_LEVELS >= 4) 574 /* Get the PXE and check if it's valid */ 575 PointerPxe = MiAddressToPxe((PVOID)Va); 576 if (!PointerPxe->u.Hard.Valid) 577 { 578 /* Check for unmapped range and skip it */ 579 if (!PointerPxe->u.Long) 580 { 581 /* There are gaps in the address space */ 582 AddressGap = TRUE; 583 584 /* Update Va and continue looping */ 585 Va = (ULONG_PTR)MiPxeToAddress(PointerPxe + 1); 586 continue; 587 } 588 589 /* Make the PXE valid */ 590 MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), CurrentProcess); 591 } 592 #endif 593 #if (_MI_PAGING_LEVELS >= 3) 594 /* Get the PPE and check if it's valid */ 595 PointerPpe = MiAddressToPpe((PVOID)Va); 596 if (!PointerPpe->u.Hard.Valid) 597 { 598 /* Check for unmapped range and skip it */ 599 if (!PointerPpe->u.Long) 600 { 601 /* There are gaps in the address space */ 602 AddressGap = TRUE; 603 604 /* Update Va and continue looping */ 605 Va = (ULONG_PTR)MiPpeToAddress(PointerPpe + 1); 606 continue; 607 } 608 609 /* Make the PPE valid */ 610 MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), CurrentProcess); 611 } 612 #endif 613 /* Skip invalid PDEs */ 614 PointerPde = MiAddressToPde((PVOID)Va); 615 if (!PointerPde->u.Long) 616 { 617 /* There are gaps in the address space */ 618 AddressGap = TRUE; 619 620 /* Check if all the PDEs are invalid, so there's nothing to free */ 621 Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1); 622 continue; 623 } 624 625 /* Now check if the PDE is mapped in */ 626 if (!PointerPde->u.Hard.Valid) 627 { 628 /* It isn't, so map it in */ 629 PointerPte = MiPteToAddress(PointerPde); 630 MiMakeSystemAddressValid(PointerPte, CurrentProcess); 631 } 632 633 /* Now we should have a valid PDE, mapped in, and still have some VA */ 634 ASSERT(PointerPde->u.Hard.Valid == 1); 635 ASSERT(Va <= EndingAddress); 636 637 /* Check if this is a section VAD with gaps in it */ 638 if ((AddressGap) && (LastPrototypePte)) 639 { 640 /* We need to skip to the next correct prototype PTE */ 641 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); 642 643 /* And we need the subsection to skip to the next last prototype PTE */ 644 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); 645 if (Subsection) 646 { 647 /* Found it! */ 648 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 649 } 650 else 651 { 652 /* No more subsections, we are done with prototype PTEs */ 653 PrototypePte = NULL; 654 } 655 } 656 657 /* Lock the PFN Database while we delete the PTEs */ 658 OldIrql = MiAcquirePfnLock(); 659 PointerPte = MiAddressToPte(Va); 660 do 661 { 662 /* Making sure the PDE is still valid */ 663 ASSERT(PointerPde->u.Hard.Valid == 1); 664 665 /* Capture the PDE and make sure it exists */ 666 TempPte = *PointerPte; 667 if (TempPte.u.Long) 668 { 669 /* Check if the PTE is actually mapped in */ 670 if (MI_IS_MAPPED_PTE(&TempPte)) 671 { 672 /* Are we dealing with section VAD? */ 673 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte)) 674 { 675 /* We need to skip to the next correct prototype PTE */ 676 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); 677 678 /* And we need the subsection to skip to the next last prototype PTE */ 679 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); 680 if (Subsection) 681 { 682 /* Found it! */ 683 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 684 } 685 else 686 { 687 /* No more subsections, we are done with prototype PTEs */ 688 PrototypePte = NULL; 689 } 690 } 691 692 /* Check for prototype PTE */ 693 if ((TempPte.u.Hard.Valid == 0) && 694 (TempPte.u.Soft.Prototype == 1)) 695 { 696 /* Just nuke it */ 697 MI_ERASE_PTE(PointerPte); 698 } 699 else 700 { 701 /* Delete the PTE proper */ 702 MiDeletePte(PointerPte, 703 (PVOID)Va, 704 CurrentProcess, 705 PrototypePte); 706 } 707 } 708 else 709 { 710 /* The PTE was never mapped, just nuke it here */ 711 MI_ERASE_PTE(PointerPte); 712 } 713 714 if (MiDecrementPageTableReferences((PVOID)Va) == 0) 715 { 716 ASSERT(PointerPde->u.Long != 0); 717 718 /* Delete the PDE proper */ 719 MiDeletePde(PointerPde, CurrentProcess); 720 721 /* Continue with the next PDE */ 722 Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1); 723 724 /* Use this to detect address gaps */ 725 PointerPte++; 726 break; 727 } 728 } 729 730 /* Update the address and PTE for it */ 731 Va += PAGE_SIZE; 732 PointerPte++; 733 PrototypePte++; 734 } while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress)); 735 736 /* Release the lock */ 737 MiReleasePfnLock(OldIrql); 738 739 if (Va > EndingAddress) return; 740 741 /* Check if we exited the loop regularly */ 742 AddressGap = (PointerPte != MiAddressToPte(Va)); 743 } 744 } 745 746 LONG 747 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo, 748 OUT PBOOLEAN HaveBadAddress, 749 OUT PULONG_PTR BadAddress) 750 { 751 PEXCEPTION_RECORD ExceptionRecord; 752 PAGED_CODE(); 753 754 // 755 // Assume default 756 // 757 *HaveBadAddress = FALSE; 758 759 // 760 // Get the exception record 761 // 762 ExceptionRecord = ExceptionInfo->ExceptionRecord; 763 764 // 765 // Look at the exception code 766 // 767 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || 768 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || 769 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) 770 { 771 // 772 // We can tell the address if we have more than one parameter 773 // 774 if (ExceptionRecord->NumberParameters > 1) 775 { 776 // 777 // Return the address 778 // 779 *HaveBadAddress = TRUE; 780 *BadAddress = ExceptionRecord->ExceptionInformation[1]; 781 } 782 } 783 784 // 785 // Continue executing the next handler 786 // 787 return EXCEPTION_EXECUTE_HANDLER; 788 } 789 790 NTSTATUS 791 NTAPI 792 MiDoMappedCopy(IN PEPROCESS SourceProcess, 793 IN PVOID SourceAddress, 794 IN PEPROCESS TargetProcess, 795 OUT PVOID TargetAddress, 796 IN SIZE_T BufferSize, 797 IN KPROCESSOR_MODE PreviousMode, 798 OUT PSIZE_T ReturnSize) 799 { 800 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1]; 801 PMDL Mdl = (PMDL)MdlBuffer; 802 SIZE_T TotalSize, CurrentSize, RemainingSize; 803 volatile BOOLEAN FailedInProbe = FALSE; 804 volatile BOOLEAN PagesLocked = FALSE; 805 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; 806 volatile PVOID MdlAddress = NULL; 807 KAPC_STATE ApcState; 808 BOOLEAN HaveBadAddress; 809 ULONG_PTR BadAddress; 810 NTSTATUS Status = STATUS_SUCCESS; 811 PAGED_CODE(); 812 813 // 814 // Calculate the maximum amount of data to move 815 // 816 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE; 817 if (BufferSize <= TotalSize) TotalSize = BufferSize; 818 CurrentSize = TotalSize; 819 RemainingSize = BufferSize; 820 821 // 822 // Loop as long as there is still data 823 // 824 while (RemainingSize > 0) 825 { 826 // 827 // Check if this transfer will finish everything off 828 // 829 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; 830 831 // 832 // Attach to the source address space 833 // 834 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); 835 836 // 837 // Check state for this pass 838 // 839 ASSERT(MdlAddress == NULL); 840 ASSERT(PagesLocked == FALSE); 841 ASSERT(FailedInProbe == FALSE); 842 843 // 844 // Protect user-mode copy 845 // 846 _SEH2_TRY 847 { 848 // 849 // If this is our first time, probe the buffer 850 // 851 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) 852 { 853 // 854 // Catch a failure here 855 // 856 FailedInProbe = TRUE; 857 858 // 859 // Do the probe 860 // 861 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); 862 863 // 864 // Passed 865 // 866 FailedInProbe = FALSE; 867 } 868 869 // 870 // Initialize and probe and lock the MDL 871 // 872 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize); 873 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess); 874 PagesLocked = TRUE; 875 } 876 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 877 { 878 Status = _SEH2_GetExceptionCode(); 879 } 880 _SEH2_END 881 882 /* Detach from source process */ 883 KeUnstackDetachProcess(&ApcState); 884 885 if (Status != STATUS_SUCCESS) 886 { 887 goto Exit; 888 } 889 890 // 891 // Now map the pages 892 // 893 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl, 894 KernelMode, 895 MmCached, 896 NULL, 897 FALSE, 898 HighPagePriority); 899 if (!MdlAddress) 900 { 901 Status = STATUS_INSUFFICIENT_RESOURCES; 902 goto Exit; 903 } 904 905 // 906 // Grab to the target process 907 // 908 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 909 910 _SEH2_TRY 911 { 912 // 913 // Check if this is our first time through 914 // 915 if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode)) 916 { 917 // 918 // Catch a failure here 919 // 920 FailedInProbe = TRUE; 921 922 // 923 // Do the probe 924 // 925 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); 926 927 // 928 // Passed 929 // 930 FailedInProbe = FALSE; 931 } 932 933 // 934 // Now do the actual move 935 // 936 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize); 937 } 938 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), 939 &HaveBadAddress, 940 &BadAddress)) 941 { 942 *ReturnSize = BufferSize - RemainingSize; 943 // 944 // Check if we failed during the probe 945 // 946 if (FailedInProbe) 947 { 948 // 949 // Exit 950 // 951 Status = _SEH2_GetExceptionCode(); 952 } 953 else 954 { 955 // 956 // Othewise we failed during the move. 957 // Check if we know exactly where we stopped copying 958 // 959 if (HaveBadAddress) 960 { 961 // 962 // Return the exact number of bytes copied 963 // 964 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; 965 } 966 // 967 // Return partial copy 968 // 969 Status = STATUS_PARTIAL_COPY; 970 } 971 } 972 _SEH2_END; 973 974 /* Detach from target process */ 975 KeUnstackDetachProcess(&ApcState); 976 977 // 978 // Check for SEH status 979 // 980 if (Status != STATUS_SUCCESS) 981 { 982 goto Exit; 983 } 984 985 // 986 // Unmap and unlock 987 // 988 MmUnmapLockedPages(MdlAddress, Mdl); 989 MdlAddress = NULL; 990 MmUnlockPages(Mdl); 991 PagesLocked = FALSE; 992 993 // 994 // Update location and size 995 // 996 RemainingSize -= CurrentSize; 997 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); 998 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize); 999 } 1000 1001 Exit: 1002 if (MdlAddress != NULL) 1003 MmUnmapLockedPages(MdlAddress, Mdl); 1004 if (PagesLocked) 1005 MmUnlockPages(Mdl); 1006 1007 // 1008 // All bytes read 1009 // 1010 if (Status == STATUS_SUCCESS) 1011 *ReturnSize = BufferSize; 1012 return Status; 1013 } 1014 1015 NTSTATUS 1016 NTAPI 1017 MiDoPoolCopy(IN PEPROCESS SourceProcess, 1018 IN PVOID SourceAddress, 1019 IN PEPROCESS TargetProcess, 1020 OUT PVOID TargetAddress, 1021 IN SIZE_T BufferSize, 1022 IN KPROCESSOR_MODE PreviousMode, 1023 OUT PSIZE_T ReturnSize) 1024 { 1025 UCHAR StackBuffer[MI_POOL_COPY_BYTES]; 1026 SIZE_T TotalSize, CurrentSize, RemainingSize; 1027 volatile BOOLEAN FailedInProbe = FALSE, HavePoolAddress = FALSE; 1028 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; 1029 PVOID PoolAddress; 1030 KAPC_STATE ApcState; 1031 BOOLEAN HaveBadAddress; 1032 ULONG_PTR BadAddress; 1033 NTSTATUS Status = STATUS_SUCCESS; 1034 PAGED_CODE(); 1035 1036 DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n", 1037 BufferSize, SourceProcess, SourceAddress, TargetProcess, TargetAddress); 1038 1039 // 1040 // Calculate the maximum amount of data to move 1041 // 1042 TotalSize = MI_MAX_TRANSFER_SIZE; 1043 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize; 1044 CurrentSize = TotalSize; 1045 RemainingSize = BufferSize; 1046 1047 // 1048 // Check if we can use the stack 1049 // 1050 if (BufferSize <= MI_POOL_COPY_BYTES) 1051 { 1052 // 1053 // Use it 1054 // 1055 PoolAddress = (PVOID)StackBuffer; 1056 } 1057 else 1058 { 1059 // 1060 // Allocate pool 1061 // 1062 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw'); 1063 if (!PoolAddress) ASSERT(FALSE); 1064 HavePoolAddress = TRUE; 1065 } 1066 1067 // 1068 // Loop as long as there is still data 1069 // 1070 while (RemainingSize > 0) 1071 { 1072 // 1073 // Check if this transfer will finish everything off 1074 // 1075 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; 1076 1077 // 1078 // Attach to the source address space 1079 // 1080 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); 1081 1082 /* Check that state is sane */ 1083 ASSERT(FailedInProbe == FALSE); 1084 ASSERT(Status == STATUS_SUCCESS); 1085 1086 // 1087 // Protect user-mode copy 1088 // 1089 _SEH2_TRY 1090 { 1091 // 1092 // If this is our first time, probe the buffer 1093 // 1094 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) 1095 { 1096 // 1097 // Catch a failure here 1098 // 1099 FailedInProbe = TRUE; 1100 1101 // 1102 // Do the probe 1103 // 1104 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); 1105 1106 // 1107 // Passed 1108 // 1109 FailedInProbe = FALSE; 1110 } 1111 1112 // 1113 // Do the copy 1114 // 1115 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize); 1116 } 1117 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), 1118 &HaveBadAddress, 1119 &BadAddress)) 1120 { 1121 *ReturnSize = BufferSize - RemainingSize; 1122 1123 // 1124 // Check if we failed during the probe 1125 // 1126 if (FailedInProbe) 1127 { 1128 // 1129 // Exit 1130 // 1131 Status = _SEH2_GetExceptionCode(); 1132 } 1133 else 1134 { 1135 // 1136 // We failed during the move. 1137 // Check if we know exactly where we stopped copying 1138 // 1139 if (HaveBadAddress) 1140 { 1141 // 1142 // Return the exact number of bytes copied 1143 // 1144 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; 1145 } 1146 // 1147 // Return partial copy 1148 // 1149 Status = STATUS_PARTIAL_COPY; 1150 } 1151 } 1152 _SEH2_END 1153 1154 /* Let go of the source */ 1155 KeUnstackDetachProcess(&ApcState); 1156 1157 if (Status != STATUS_SUCCESS) 1158 { 1159 goto Exit; 1160 } 1161 1162 /* Grab the target process */ 1163 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 1164 1165 _SEH2_TRY 1166 { 1167 // 1168 // Check if this is our first time through 1169 // 1170 if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode)) 1171 { 1172 // 1173 // Catch a failure here 1174 // 1175 FailedInProbe = TRUE; 1176 1177 // 1178 // Do the probe 1179 // 1180 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); 1181 1182 // 1183 // Passed 1184 // 1185 FailedInProbe = FALSE; 1186 } 1187 1188 // 1189 // Now do the actual move 1190 // 1191 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize); 1192 } 1193 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), 1194 &HaveBadAddress, 1195 &BadAddress)) 1196 { 1197 *ReturnSize = BufferSize - RemainingSize; 1198 // 1199 // Check if we failed during the probe 1200 // 1201 if (FailedInProbe) 1202 { 1203 // 1204 // Exit 1205 // 1206 Status = _SEH2_GetExceptionCode(); 1207 } 1208 else 1209 { 1210 // 1211 // Otherwise we failed during the move. 1212 // Check if we know exactly where we stopped copying 1213 // 1214 if (HaveBadAddress) 1215 { 1216 // 1217 // Return the exact number of bytes copied 1218 // 1219 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; 1220 } 1221 // 1222 // Return partial copy 1223 // 1224 Status = STATUS_PARTIAL_COPY; 1225 } 1226 } 1227 _SEH2_END; 1228 1229 // 1230 // Detach from target 1231 // 1232 KeUnstackDetachProcess(&ApcState); 1233 1234 // 1235 // Check for SEH status 1236 // 1237 if (Status != STATUS_SUCCESS) 1238 { 1239 goto Exit; 1240 } 1241 1242 // 1243 // Update location and size 1244 // 1245 RemainingSize -= CurrentSize; 1246 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); 1247 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + 1248 CurrentSize); 1249 } 1250 1251 Exit: 1252 // 1253 // Check if we had allocated pool 1254 // 1255 if (HavePoolAddress) 1256 ExFreePoolWithTag(PoolAddress, 'VmRw'); 1257 1258 // 1259 // All bytes read 1260 // 1261 if (Status == STATUS_SUCCESS) 1262 *ReturnSize = BufferSize; 1263 return Status; 1264 } 1265 1266 NTSTATUS 1267 NTAPI 1268 MmCopyVirtualMemory(IN PEPROCESS SourceProcess, 1269 IN PVOID SourceAddress, 1270 IN PEPROCESS TargetProcess, 1271 OUT PVOID TargetAddress, 1272 IN SIZE_T BufferSize, 1273 IN KPROCESSOR_MODE PreviousMode, 1274 OUT PSIZE_T ReturnSize) 1275 { 1276 NTSTATUS Status; 1277 PEPROCESS Process = SourceProcess; 1278 1279 // 1280 // Don't accept zero-sized buffers 1281 // 1282 if (!BufferSize) return STATUS_SUCCESS; 1283 1284 // 1285 // If we are copying from ourselves, lock the target instead 1286 // 1287 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess; 1288 1289 // 1290 // Acquire rundown protection 1291 // 1292 if (!ExAcquireRundownProtection(&Process->RundownProtect)) 1293 { 1294 // 1295 // Fail 1296 // 1297 return STATUS_PROCESS_IS_TERMINATING; 1298 } 1299 1300 // 1301 // See if we should use the pool copy 1302 // 1303 if (BufferSize > MI_POOL_COPY_BYTES) 1304 { 1305 // 1306 // Use MDL-copy 1307 // 1308 Status = MiDoMappedCopy(SourceProcess, 1309 SourceAddress, 1310 TargetProcess, 1311 TargetAddress, 1312 BufferSize, 1313 PreviousMode, 1314 ReturnSize); 1315 } 1316 else 1317 { 1318 // 1319 // Do pool copy 1320 // 1321 Status = MiDoPoolCopy(SourceProcess, 1322 SourceAddress, 1323 TargetProcess, 1324 TargetAddress, 1325 BufferSize, 1326 PreviousMode, 1327 ReturnSize); 1328 } 1329 1330 // 1331 // Release the lock 1332 // 1333 ExReleaseRundownProtection(&Process->RundownProtect); 1334 return Status; 1335 } 1336 1337 NTSTATUS 1338 NTAPI 1339 MmFlushVirtualMemory(IN PEPROCESS Process, 1340 IN OUT PVOID *BaseAddress, 1341 IN OUT PSIZE_T RegionSize, 1342 OUT PIO_STATUS_BLOCK IoStatusBlock) 1343 { 1344 PAGED_CODE(); 1345 1346 UNIMPLEMENTED; 1347 1348 return STATUS_NOT_IMPLEMENTED; 1349 } 1350 1351 ULONG 1352 NTAPI 1353 MiGetPageProtection(IN PMMPTE PointerPte) 1354 { 1355 MMPTE TempPte; 1356 PMMPFN Pfn; 1357 PEPROCESS CurrentProcess; 1358 PETHREAD CurrentThread; 1359 BOOLEAN WsSafe, WsShared; 1360 ULONG Protect; 1361 KIRQL OldIrql; 1362 PAGED_CODE(); 1363 1364 /* Copy this PTE's contents */ 1365 TempPte = *PointerPte; 1366 1367 /* Assure it's not totally zero */ 1368 ASSERT(TempPte.u.Long); 1369 1370 /* Check for a special prototype format */ 1371 if ((TempPte.u.Soft.Valid == 0) && 1372 (TempPte.u.Soft.Prototype == 1)) 1373 { 1374 /* Check if the prototype PTE is not yet pointing to a PTE */ 1375 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 1376 { 1377 /* The prototype PTE contains the protection */ 1378 return MmProtectToValue[TempPte.u.Soft.Protection]; 1379 } 1380 1381 /* Get a pointer to the underlying shared PTE */ 1382 PointerPte = MiProtoPteToPte(&TempPte); 1383 1384 /* Since the PTE we want to read can be paged out at any time, we need 1385 to release the working set lock first, so that it can be paged in */ 1386 CurrentThread = PsGetCurrentThread(); 1387 CurrentProcess = PsGetCurrentProcess(); 1388 MiUnlockProcessWorkingSetForFault(CurrentProcess, 1389 CurrentThread, 1390 &WsSafe, 1391 &WsShared); 1392 1393 /* Now read the PTE value */ 1394 TempPte = *PointerPte; 1395 1396 /* Check if that one is invalid */ 1397 if (!TempPte.u.Hard.Valid) 1398 { 1399 /* We get the protection directly from this PTE */ 1400 Protect = MmProtectToValue[TempPte.u.Soft.Protection]; 1401 } 1402 else 1403 { 1404 /* The PTE is valid, so we might need to get the protection from 1405 the PFN. Lock the PFN database */ 1406 OldIrql = MiAcquirePfnLock(); 1407 1408 /* Check if the PDE is still valid */ 1409 if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0) 1410 { 1411 /* It's not, make it valid */ 1412 MiMakeSystemAddressValidPfn(PointerPte, OldIrql); 1413 } 1414 1415 /* Now it's safe to read the PTE value again */ 1416 TempPte = *PointerPte; 1417 ASSERT(TempPte.u.Long != 0); 1418 1419 /* Check again if the PTE is invalid */ 1420 if (!TempPte.u.Hard.Valid) 1421 { 1422 /* The PTE is not valid, so we can use it's protection field */ 1423 Protect = MmProtectToValue[TempPte.u.Soft.Protection]; 1424 } 1425 else 1426 { 1427 /* The PTE is valid, so we can find the protection in the 1428 OriginalPte field of the PFN */ 1429 Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber); 1430 Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection]; 1431 } 1432 1433 /* Release the PFN database */ 1434 MiReleasePfnLock(OldIrql); 1435 } 1436 1437 /* Lock the working set again */ 1438 MiLockProcessWorkingSetForFault(CurrentProcess, 1439 CurrentThread, 1440 WsSafe, 1441 WsShared); 1442 1443 return Protect; 1444 } 1445 1446 /* In the easy case of transition or demand zero PTE just return its protection */ 1447 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection]; 1448 1449 /* If we get here, the PTE is valid, so look up the page in PFN database */ 1450 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber); 1451 if (!Pfn->u3.e1.PrototypePte) 1452 { 1453 /* Return protection of the original pte */ 1454 ASSERT(Pfn->u4.AweAllocation == 0); 1455 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection]; 1456 } 1457 1458 /* This is software PTE */ 1459 DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn); 1460 DPRINT("VA: %p\n", MiPteToAddress(&TempPte)); 1461 DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection); 1462 DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection); 1463 return MmProtectToValue[TempPte.u.Soft.Protection]; 1464 } 1465 1466 ULONG 1467 NTAPI 1468 MiQueryAddressState(IN PVOID Va, 1469 IN PMMVAD Vad, 1470 IN PEPROCESS TargetProcess, 1471 OUT PULONG ReturnedProtect, 1472 OUT PVOID *NextVa) 1473 { 1474 1475 PMMPTE PointerPte, ProtoPte; 1476 PMMPDE PointerPde; 1477 #if (_MI_PAGING_LEVELS >= 3) 1478 PMMPPE PointerPpe; 1479 #endif 1480 #if (_MI_PAGING_LEVELS >= 4) 1481 PMMPXE PointerPxe; 1482 #endif 1483 MMPTE TempPte, TempProtoPte; 1484 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE; 1485 ULONG State = MEM_RESERVE, Protect = 0; 1486 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) && 1487 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT))); 1488 1489 /* Only normal VADs supported */ 1490 ASSERT(Vad->u.VadFlags.VadType == VadNone); 1491 1492 /* Get the PDE and PTE for the address */ 1493 PointerPde = MiAddressToPde(Va); 1494 PointerPte = MiAddressToPte(Va); 1495 #if (_MI_PAGING_LEVELS >= 3) 1496 PointerPpe = MiAddressToPpe(Va); 1497 #endif 1498 #if (_MI_PAGING_LEVELS >= 4) 1499 PointerPxe = MiAddressToPxe(Va); 1500 #endif 1501 1502 /* Return the next range */ 1503 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE); 1504 1505 do 1506 { 1507 #if (_MI_PAGING_LEVELS >= 4) 1508 /* Does the PXE exist? */ 1509 if (PointerPxe->u.Long == 0) 1510 { 1511 /* It does not, next range starts at the next PXE */ 1512 *NextVa = MiPxeToAddress(PointerPxe + 1); 1513 break; 1514 } 1515 1516 /* Is the PXE valid? */ 1517 if (PointerPxe->u.Hard.Valid == 0) 1518 { 1519 /* Is isn't, fault it in (make the PPE accessible) */ 1520 MiMakeSystemAddressValid(PointerPpe, TargetProcess); 1521 } 1522 #endif 1523 #if (_MI_PAGING_LEVELS >= 3) 1524 /* Does the PPE exist? */ 1525 if (PointerPpe->u.Long == 0) 1526 { 1527 /* It does not, next range starts at the next PPE */ 1528 *NextVa = MiPpeToAddress(PointerPpe + 1); 1529 break; 1530 } 1531 1532 /* Is the PPE valid? */ 1533 if (PointerPpe->u.Hard.Valid == 0) 1534 { 1535 /* Is isn't, fault it in (make the PDE accessible) */ 1536 MiMakeSystemAddressValid(PointerPde, TargetProcess); 1537 } 1538 #endif 1539 1540 /* Does the PDE exist? */ 1541 if (PointerPde->u.Long == 0) 1542 { 1543 /* It does not, next range starts at the next PDE */ 1544 *NextVa = MiPdeToAddress(PointerPde + 1); 1545 break; 1546 } 1547 1548 /* Is the PDE valid? */ 1549 if (PointerPde->u.Hard.Valid == 0) 1550 { 1551 /* Is isn't, fault it in (make the PTE accessible) */ 1552 MiMakeSystemAddressValid(PointerPte, TargetProcess); 1553 } 1554 1555 /* We have a PTE that we can access now! */ 1556 ValidPte = TRUE; 1557 1558 } while (FALSE); 1559 1560 /* Is it safe to try reading the PTE? */ 1561 if (ValidPte) 1562 { 1563 /* FIXME: watch out for large pages */ 1564 ASSERT(PointerPde->u.Hard.LargePage == FALSE); 1565 1566 /* Capture the PTE */ 1567 TempPte = *PointerPte; 1568 if (TempPte.u.Long != 0) 1569 { 1570 /* The PTE is valid, so it's not zeroed out */ 1571 DemandZeroPte = FALSE; 1572 1573 /* Is it a decommited, invalid, or faulted PTE? */ 1574 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) && 1575 (TempPte.u.Hard.Valid == 0) && 1576 ((TempPte.u.Soft.Prototype == 0) || 1577 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 1578 { 1579 /* Otherwise our defaults should hold */ 1580 ASSERT(Protect == 0); 1581 ASSERT(State == MEM_RESERVE); 1582 } 1583 else 1584 { 1585 /* This means it's committed */ 1586 State = MEM_COMMIT; 1587 1588 /* We don't support these */ 1589 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory); 1590 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical); 1591 ASSERT(Vad->u.VadFlags.VadType != VadAwe); 1592 1593 /* Get protection state of this page */ 1594 Protect = MiGetPageProtection(PointerPte); 1595 1596 /* Check if this is an image-backed VAD */ 1597 if ((TempPte.u.Soft.Valid == 0) && 1598 (TempPte.u.Soft.Prototype == 1) && 1599 (Vad->u.VadFlags.PrivateMemory == 0) && 1600 (Vad->ControlArea)) 1601 { 1602 DPRINT1("Not supported\n"); 1603 ASSERT(FALSE); 1604 } 1605 } 1606 } 1607 } 1608 1609 /* Check if this was a demand-zero PTE, since we need to find the state */ 1610 if (DemandZeroPte) 1611 { 1612 /* Not yet handled */ 1613 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory); 1614 ASSERT(Vad->u.VadFlags.VadType != VadAwe); 1615 1616 /* Check if this is private commited memory, or an section-backed VAD */ 1617 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea)) 1618 { 1619 /* Tell caller about the next range */ 1620 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE); 1621 1622 /* Get the prototype PTE for this VAD */ 1623 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, 1624 (ULONG_PTR)Va >> PAGE_SHIFT); 1625 if (ProtoPte) 1626 { 1627 /* We should unlock the working set, but it's not being held! */ 1628 1629 /* Is the prototype PTE actually valid (committed)? */ 1630 TempProtoPte = *ProtoPte; 1631 if (TempProtoPte.u.Long) 1632 { 1633 /* Unless this is a memory-mapped file, handle it like private VAD */ 1634 State = MEM_COMMIT; 1635 ASSERT(Vad->u.VadFlags.VadType != VadImageMap); 1636 Protect = MmProtectToValue[Vad->u.VadFlags.Protection]; 1637 } 1638 1639 /* We should re-lock the working set */ 1640 } 1641 } 1642 else if (Vad->u.VadFlags.MemCommit) 1643 { 1644 /* This is committed memory */ 1645 State = MEM_COMMIT; 1646 1647 /* Convert the protection */ 1648 Protect = MmProtectToValue[Vad->u.VadFlags.Protection]; 1649 } 1650 } 1651 1652 /* Return the protection code */ 1653 *ReturnedProtect = Protect; 1654 return State; 1655 } 1656 1657 NTSTATUS 1658 NTAPI 1659 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle, 1660 IN PVOID BaseAddress, 1661 OUT PVOID MemoryInformation, 1662 IN SIZE_T MemoryInformationLength, 1663 OUT PSIZE_T ReturnLength) 1664 { 1665 PEPROCESS TargetProcess; 1666 NTSTATUS Status = STATUS_SUCCESS; 1667 PMMVAD Vad = NULL; 1668 PVOID Address, NextAddress; 1669 BOOLEAN Found = FALSE; 1670 ULONG NewProtect, NewState; 1671 ULONG_PTR BaseVpn; 1672 MEMORY_BASIC_INFORMATION MemoryInfo; 1673 KAPC_STATE ApcState; 1674 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 1675 PMEMORY_AREA MemoryArea; 1676 SIZE_T ResultLength; 1677 1678 /* Check for illegal addresses in user-space, or the shared memory area */ 1679 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) || 1680 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)) 1681 { 1682 Address = PAGE_ALIGN(BaseAddress); 1683 1684 /* Make up an info structure describing this range */ 1685 MemoryInfo.BaseAddress = Address; 1686 MemoryInfo.AllocationProtect = PAGE_READONLY; 1687 MemoryInfo.Type = MEM_PRIVATE; 1688 1689 /* Special case for shared data */ 1690 if (Address == (PVOID)MM_SHARED_USER_DATA_VA) 1691 { 1692 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA; 1693 MemoryInfo.State = MEM_COMMIT; 1694 MemoryInfo.Protect = PAGE_READONLY; 1695 MemoryInfo.RegionSize = PAGE_SIZE; 1696 } 1697 else 1698 { 1699 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1; 1700 MemoryInfo.State = MEM_RESERVE; 1701 MemoryInfo.Protect = PAGE_NOACCESS; 1702 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address; 1703 } 1704 1705 /* Return the data, NtQueryInformation already probed it*/ 1706 if (PreviousMode != KernelMode) 1707 { 1708 _SEH2_TRY 1709 { 1710 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1711 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1712 } 1713 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1714 { 1715 Status = _SEH2_GetExceptionCode(); 1716 } 1717 _SEH2_END; 1718 } 1719 else 1720 { 1721 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1722 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1723 } 1724 1725 return Status; 1726 } 1727 1728 /* Check if this is for a local or remote process */ 1729 if (ProcessHandle == NtCurrentProcess()) 1730 { 1731 TargetProcess = PsGetCurrentProcess(); 1732 } 1733 else 1734 { 1735 /* Reference the target process */ 1736 Status = ObReferenceObjectByHandle(ProcessHandle, 1737 PROCESS_QUERY_INFORMATION, 1738 PsProcessType, 1739 ExGetPreviousMode(), 1740 (PVOID*)&TargetProcess, 1741 NULL); 1742 if (!NT_SUCCESS(Status)) return Status; 1743 1744 /* Attach to it now */ 1745 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 1746 } 1747 1748 /* Lock the address space and make sure the process isn't already dead */ 1749 MmLockAddressSpace(&TargetProcess->Vm); 1750 if (TargetProcess->VmDeleted) 1751 { 1752 /* Unlock the address space of the process */ 1753 MmUnlockAddressSpace(&TargetProcess->Vm); 1754 1755 /* Check if we were attached */ 1756 if (ProcessHandle != NtCurrentProcess()) 1757 { 1758 /* Detach and dereference the process */ 1759 KeUnstackDetachProcess(&ApcState); 1760 ObDereferenceObject(TargetProcess); 1761 } 1762 1763 /* Bail out */ 1764 DPRINT1("Process is dying\n"); 1765 return STATUS_PROCESS_IS_TERMINATING; 1766 } 1767 1768 /* Loop the VADs */ 1769 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements); 1770 if (TargetProcess->VadRoot.NumberGenericTableElements) 1771 { 1772 /* Scan on the right */ 1773 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild; 1774 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT; 1775 while (Vad) 1776 { 1777 /* Check if this VAD covers the allocation range */ 1778 if ((BaseVpn >= Vad->StartingVpn) && 1779 (BaseVpn <= Vad->EndingVpn)) 1780 { 1781 /* We're done */ 1782 Found = TRUE; 1783 break; 1784 } 1785 1786 /* Check if this VAD is too high */ 1787 if (BaseVpn < Vad->StartingVpn) 1788 { 1789 /* Stop if there is no left child */ 1790 if (!Vad->LeftChild) break; 1791 1792 /* Search on the left next */ 1793 Vad = Vad->LeftChild; 1794 } 1795 else 1796 { 1797 /* Then this VAD is too low, keep searching on the right */ 1798 ASSERT(BaseVpn > Vad->EndingVpn); 1799 1800 /* Stop if there is no right child */ 1801 if (!Vad->RightChild) break; 1802 1803 /* Search on the right next */ 1804 Vad = Vad->RightChild; 1805 } 1806 } 1807 } 1808 1809 /* Was a VAD found? */ 1810 if (!Found) 1811 { 1812 Address = PAGE_ALIGN(BaseAddress); 1813 1814 /* Calculate region size */ 1815 if (Vad) 1816 { 1817 if (Vad->StartingVpn >= BaseVpn) 1818 { 1819 /* Region size is the free space till the start of that VAD */ 1820 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address; 1821 } 1822 else 1823 { 1824 /* Get the next VAD */ 1825 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad); 1826 if (Vad) 1827 { 1828 /* Region size is the free space till the start of that VAD */ 1829 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address; 1830 } 1831 else 1832 { 1833 /* Maximum possible region size with that base address */ 1834 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address; 1835 } 1836 } 1837 } 1838 else 1839 { 1840 /* Maximum possible region size with that base address */ 1841 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address; 1842 } 1843 1844 /* Unlock the address space of the process */ 1845 MmUnlockAddressSpace(&TargetProcess->Vm); 1846 1847 /* Check if we were attached */ 1848 if (ProcessHandle != NtCurrentProcess()) 1849 { 1850 /* Detach and dereference the process */ 1851 KeUnstackDetachProcess(&ApcState); 1852 ObDereferenceObject(TargetProcess); 1853 } 1854 1855 /* Build the rest of the initial information block */ 1856 MemoryInfo.BaseAddress = Address; 1857 MemoryInfo.AllocationBase = NULL; 1858 MemoryInfo.AllocationProtect = 0; 1859 MemoryInfo.State = MEM_FREE; 1860 MemoryInfo.Protect = PAGE_NOACCESS; 1861 MemoryInfo.Type = 0; 1862 1863 /* Return the data, NtQueryInformation already probed it*/ 1864 if (PreviousMode != KernelMode) 1865 { 1866 _SEH2_TRY 1867 { 1868 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1869 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1870 } 1871 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1872 { 1873 Status = _SEH2_GetExceptionCode(); 1874 } 1875 _SEH2_END; 1876 } 1877 else 1878 { 1879 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1880 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1881 } 1882 1883 return Status; 1884 } 1885 1886 /* Set the correct memory type based on what kind of VAD this is */ 1887 if ((Vad->u.VadFlags.PrivateMemory) || 1888 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 1889 { 1890 MemoryInfo.Type = MEM_PRIVATE; 1891 } 1892 else if (Vad->u.VadFlags.VadType == VadImageMap) 1893 { 1894 MemoryInfo.Type = MEM_IMAGE; 1895 } 1896 else 1897 { 1898 MemoryInfo.Type = MEM_MAPPED; 1899 } 1900 1901 /* Find the memory area the specified address belongs to */ 1902 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress); 1903 ASSERT(MemoryArea != NULL); 1904 1905 /* Determine information dependent on the memory area type */ 1906 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 1907 { 1908 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength); 1909 if (!NT_SUCCESS(Status)) 1910 { 1911 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n", 1912 MemoryArea, MA_GetStartingAddress(MemoryArea), MA_GetEndingAddress(MemoryArea), BaseAddress); 1913 ASSERT(NT_SUCCESS(Status)); 1914 } 1915 } 1916 else 1917 { 1918 /* Build the initial information block */ 1919 Address = PAGE_ALIGN(BaseAddress); 1920 MemoryInfo.BaseAddress = Address; 1921 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); 1922 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; 1923 MemoryInfo.Type = MEM_PRIVATE; 1924 1925 /* Acquire the working set lock (shared is enough) */ 1926 MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread()); 1927 1928 /* Find the largest chunk of memory which has the same state and protection mask */ 1929 MemoryInfo.State = MiQueryAddressState(Address, 1930 Vad, 1931 TargetProcess, 1932 &MemoryInfo.Protect, 1933 &NextAddress); 1934 Address = NextAddress; 1935 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn) 1936 { 1937 /* Keep going unless the state or protection mask changed */ 1938 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress); 1939 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break; 1940 Address = NextAddress; 1941 } 1942 1943 /* Release the working set lock */ 1944 MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread()); 1945 1946 /* Check if we went outside of the VAD */ 1947 if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn) 1948 { 1949 /* Set the end of the VAD as the end address */ 1950 Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT); 1951 } 1952 1953 /* Now that we know the last VA address, calculate the region size */ 1954 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress); 1955 } 1956 1957 /* Unlock the address space of the process */ 1958 MmUnlockAddressSpace(&TargetProcess->Vm); 1959 1960 /* Check if we were attached */ 1961 if (ProcessHandle != NtCurrentProcess()) 1962 { 1963 /* Detach and dereference the process */ 1964 KeUnstackDetachProcess(&ApcState); 1965 ObDereferenceObject(TargetProcess); 1966 } 1967 1968 /* Return the data, NtQueryInformation already probed it */ 1969 if (PreviousMode != KernelMode) 1970 { 1971 _SEH2_TRY 1972 { 1973 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1974 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1975 } 1976 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1977 { 1978 Status = _SEH2_GetExceptionCode(); 1979 } 1980 _SEH2_END; 1981 } 1982 else 1983 { 1984 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1985 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1986 } 1987 1988 /* All went well */ 1989 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx " 1990 "State: %lx Type: %lx Size: %lx\n", 1991 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase, 1992 MemoryInfo.AllocationProtect, MemoryInfo.Protect, 1993 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize); 1994 1995 return Status; 1996 } 1997 1998 BOOLEAN 1999 NTAPI 2000 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress, 2001 IN ULONG_PTR EndingAddress, 2002 IN PMMVAD Vad, 2003 IN PEPROCESS Process) 2004 { 2005 PMMPTE PointerPte, LastPte; 2006 PMMPDE PointerPde; 2007 BOOLEAN OnPdeBoundary = TRUE; 2008 #if _MI_PAGING_LEVELS >= 3 2009 PMMPPE PointerPpe; 2010 BOOLEAN OnPpeBoundary = TRUE; 2011 #if _MI_PAGING_LEVELS == 4 2012 PMMPXE PointerPxe; 2013 BOOLEAN OnPxeBoundary = TRUE; 2014 #endif 2015 #endif 2016 2017 PAGED_CODE(); 2018 2019 /* Check that we hols the right locks */ 2020 ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive || PsGetCurrentThread()->OwnsProcessWorkingSetShared); 2021 2022 /* Get the PTE addresses */ 2023 PointerPte = MiAddressToPte(StartingAddress); 2024 LastPte = MiAddressToPte(EndingAddress); 2025 2026 /* Loop all the PTEs */ 2027 while (PointerPte <= LastPte) 2028 { 2029 #if _MI_PAGING_LEVELS == 4 2030 /* Check for new PXE boundary */ 2031 if (OnPxeBoundary) 2032 { 2033 PointerPxe = MiPteToPxe(PointerPte); 2034 2035 /* Check that this loop is sane */ 2036 ASSERT(OnPpeBoundary); 2037 ASSERT(OnPdeBoundary); 2038 2039 if (PointerPxe->u.Long != 0) 2040 { 2041 /* Make it valid if needed */ 2042 if (PointerPxe->u.Hard.Valid == 0) 2043 MiMakeSystemAddressValid(MiPteToPpe(PointerPte), Process); 2044 } 2045 else 2046 { 2047 /* Is the entire VAD committed? If not, fail */ 2048 if (!Vad->u.VadFlags.MemCommit) return FALSE; 2049 2050 PointerPxe++; 2051 PointerPte = MiPxeToPte(PointerPte); 2052 continue; 2053 } 2054 } 2055 #endif 2056 2057 #if _MI_PAGING_LEVELS >= 3 2058 /* Check for new PPE boundary */ 2059 if (OnPpeBoundary) 2060 { 2061 PointerPpe = MiPteToPpe(PointerPte); 2062 2063 /* Check that this loop is sane */ 2064 ASSERT(OnPdeBoundary); 2065 2066 if (PointerPpe->u.Long != 0) 2067 { 2068 /* Make it valid if needed */ 2069 if (PointerPpe->u.Hard.Valid == 0) 2070 MiMakeSystemAddressValid(MiPteToPde(PointerPte), Process); 2071 } 2072 else 2073 { 2074 /* Is the entire VAD committed? If not, fail */ 2075 if (!Vad->u.VadFlags.MemCommit) return FALSE; 2076 2077 PointerPpe++; 2078 PointerPte = MiPpeToPte(PointerPpe); 2079 #if _MI_PAGING_LEVELS == 4 2080 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte); 2081 #endif 2082 continue; 2083 } 2084 } 2085 #endif 2086 /* Check if we've hit a new PDE boundary */ 2087 if (OnPdeBoundary) 2088 { 2089 /* Is this PDE demand zero? */ 2090 PointerPde = MiPteToPde(PointerPte); 2091 if (PointerPde->u.Long != 0) 2092 { 2093 /* It isn't -- is it valid? */ 2094 if (PointerPde->u.Hard.Valid == 0) 2095 { 2096 /* Nope, fault it in */ 2097 MiMakeSystemAddressValid(PointerPte, Process); 2098 } 2099 } 2100 else 2101 { 2102 /* Is the entire VAD committed? If not, fail */ 2103 if (!Vad->u.VadFlags.MemCommit) return FALSE; 2104 2105 /* The PTE was already valid, so move to the next one */ 2106 PointerPde++; 2107 PointerPte = MiPdeToPte(PointerPde); 2108 #if _MI_PAGING_LEVELS >= 3 2109 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte); 2110 #if _MI_PAGING_LEVELS == 4 2111 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte); 2112 #endif 2113 #endif 2114 2115 /* New loop iteration with our new, on-boundary PTE. */ 2116 continue; 2117 } 2118 } 2119 2120 /* Is the PTE demand zero? */ 2121 if (PointerPte->u.Long == 0) 2122 { 2123 /* Is the entire VAD committed? If not, fail */ 2124 if (!Vad->u.VadFlags.MemCommit) return FALSE; 2125 } 2126 else 2127 { 2128 /* It isn't -- is it a decommited, invalid, or faulted PTE? */ 2129 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) && 2130 (PointerPte->u.Hard.Valid == 0) && 2131 ((PointerPte->u.Soft.Prototype == 0) || 2132 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 2133 { 2134 /* Then part of the range is decommitted, so fail */ 2135 return FALSE; 2136 } 2137 } 2138 2139 /* Move to the next PTE */ 2140 PointerPte++; 2141 OnPdeBoundary = MiIsPteOnPdeBoundary(PointerPte); 2142 #if _MI_PAGING_LEVELS >= 3 2143 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte); 2144 #if _MI_PAGING_LEVELS == 4 2145 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte); 2146 #endif 2147 #endif 2148 } 2149 2150 /* All PTEs seem valid, and no VAD checks failed, the range is okay */ 2151 return TRUE; 2152 } 2153 2154 NTSTATUS 2155 NTAPI 2156 MiRosProtectVirtualMemory(IN PEPROCESS Process, 2157 IN OUT PVOID *BaseAddress, 2158 IN OUT PSIZE_T NumberOfBytesToProtect, 2159 IN ULONG NewAccessProtection, 2160 OUT PULONG OldAccessProtection OPTIONAL) 2161 { 2162 PMEMORY_AREA MemoryArea; 2163 PMMSUPPORT AddressSpace; 2164 ULONG OldAccessProtection_; 2165 NTSTATUS Status; 2166 2167 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress); 2168 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress); 2169 2170 AddressSpace = &Process->Vm; 2171 MmLockAddressSpace(AddressSpace); 2172 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress); 2173 if (MemoryArea == NULL || MemoryArea->DeleteInProgress) 2174 { 2175 MmUnlockAddressSpace(AddressSpace); 2176 return STATUS_UNSUCCESSFUL; 2177 } 2178 2179 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_; 2180 2181 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW); 2182 Status = MmProtectSectionView(AddressSpace, 2183 MemoryArea, 2184 *BaseAddress, 2185 *NumberOfBytesToProtect, 2186 NewAccessProtection, 2187 OldAccessProtection); 2188 2189 MmUnlockAddressSpace(AddressSpace); 2190 2191 return Status; 2192 } 2193 2194 NTSTATUS 2195 NTAPI 2196 MiProtectVirtualMemory(IN PEPROCESS Process, 2197 IN OUT PVOID *BaseAddress, 2198 IN OUT PSIZE_T NumberOfBytesToProtect, 2199 IN ULONG NewAccessProtection, 2200 OUT PULONG OldAccessProtection OPTIONAL) 2201 { 2202 PMEMORY_AREA MemoryArea; 2203 PMMVAD Vad; 2204 PMMSUPPORT AddressSpace; 2205 ULONG_PTR StartingAddress, EndingAddress; 2206 PMMPTE PointerPte, LastPte; 2207 PMMPDE PointerPde; 2208 MMPTE PteContents; 2209 PMMPFN Pfn1; 2210 ULONG ProtectionMask, OldProtect; 2211 BOOLEAN Committed; 2212 NTSTATUS Status = STATUS_SUCCESS; 2213 PETHREAD Thread = PsGetCurrentThread(); 2214 TABLE_SEARCH_RESULT Result; 2215 2216 /* We must be attached */ 2217 ASSERT(Process == PsGetCurrentProcess()); 2218 2219 /* Calculate base address for the VAD */ 2220 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress)); 2221 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1)); 2222 2223 /* Calculate the protection mask and make sure it's valid */ 2224 ProtectionMask = MiMakeProtectionMask(NewAccessProtection); 2225 if (ProtectionMask == MM_INVALID_PROTECTION) 2226 { 2227 DPRINT1("Invalid protection mask\n"); 2228 return STATUS_INVALID_PAGE_PROTECTION; 2229 } 2230 2231 /* Lock the address space and make sure the process isn't already dead */ 2232 AddressSpace = MmGetCurrentAddressSpace(); 2233 MmLockAddressSpace(AddressSpace); 2234 if (Process->VmDeleted) 2235 { 2236 DPRINT1("Process is dying\n"); 2237 Status = STATUS_PROCESS_IS_TERMINATING; 2238 goto FailPath; 2239 } 2240 2241 /* Check for ROS specific memory area */ 2242 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress); 2243 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) 2244 { 2245 /* Evil hack */ 2246 MmUnlockAddressSpace(AddressSpace); 2247 return MiRosProtectVirtualMemory(Process, 2248 BaseAddress, 2249 NumberOfBytesToProtect, 2250 NewAccessProtection, 2251 OldAccessProtection); 2252 } 2253 2254 /* Get the VAD for this address range, and make sure it exists */ 2255 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 2256 EndingAddress >> PAGE_SHIFT, 2257 &Process->VadRoot, 2258 (PMMADDRESS_NODE*)&Vad); 2259 if (Result != TableFoundNode) 2260 { 2261 DPRINT("Could not find a VAD for this allocation\n"); 2262 Status = STATUS_CONFLICTING_ADDRESSES; 2263 goto FailPath; 2264 } 2265 2266 /* Make sure the address is within this VAD's boundaries */ 2267 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) || 2268 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn)) 2269 { 2270 Status = STATUS_CONFLICTING_ADDRESSES; 2271 goto FailPath; 2272 } 2273 2274 /* These kinds of VADs are not supported atm */ 2275 if ((Vad->u.VadFlags.VadType == VadAwe) || 2276 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 2277 (Vad->u.VadFlags.VadType == VadLargePages)) 2278 { 2279 DPRINT1("Illegal VAD for attempting to set protection\n"); 2280 Status = STATUS_CONFLICTING_ADDRESSES; 2281 goto FailPath; 2282 } 2283 2284 /* Check for a VAD whose protection can't be changed */ 2285 if (Vad->u.VadFlags.NoChange == 1) 2286 { 2287 DPRINT1("Trying to change protection of a NoChange VAD\n"); 2288 Status = STATUS_INVALID_PAGE_PROTECTION; 2289 goto FailPath; 2290 } 2291 2292 /* Is this section, or private memory? */ 2293 if (Vad->u.VadFlags.PrivateMemory == 0) 2294 { 2295 /* Not yet supported */ 2296 if (Vad->u.VadFlags.VadType == VadLargePageSection) 2297 { 2298 DPRINT1("Illegal VAD for attempting to set protection\n"); 2299 Status = STATUS_CONFLICTING_ADDRESSES; 2300 goto FailPath; 2301 } 2302 2303 /* Rotate VADs are not yet supported */ 2304 if (Vad->u.VadFlags.VadType == VadRotatePhysical) 2305 { 2306 DPRINT1("Illegal VAD for attempting to set protection\n"); 2307 Status = STATUS_CONFLICTING_ADDRESSES; 2308 goto FailPath; 2309 } 2310 2311 /* Not valid on section files */ 2312 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) 2313 { 2314 /* Fail */ 2315 DPRINT1("Invalid protection flags for section\n"); 2316 Status = STATUS_INVALID_PARAMETER_4; 2317 goto FailPath; 2318 } 2319 2320 /* Check if data or page file mapping protection PTE is compatible */ 2321 if (!Vad->ControlArea->u.Flags.Image) 2322 { 2323 /* Not yet */ 2324 DPRINT1("Fixme: Not checking for valid protection\n"); 2325 } 2326 2327 /* This is a section, and this is not yet supported */ 2328 DPRINT1("Section protection not yet supported\n"); 2329 OldProtect = 0; 2330 } 2331 else 2332 { 2333 /* Private memory, check protection flags */ 2334 if ((NewAccessProtection & PAGE_WRITECOPY) || 2335 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY)) 2336 { 2337 DPRINT1("Invalid protection flags for private memory\n"); 2338 Status = STATUS_INVALID_PARAMETER_4; 2339 goto FailPath; 2340 } 2341 2342 /* Lock the working set */ 2343 MiLockProcessWorkingSetUnsafe(Process, Thread); 2344 2345 /* Check if all pages in this range are committed */ 2346 Committed = MiIsEntireRangeCommitted(StartingAddress, 2347 EndingAddress, 2348 Vad, 2349 Process); 2350 if (!Committed) 2351 { 2352 /* Fail */ 2353 DPRINT1("The entire range is not committed\n"); 2354 Status = STATUS_NOT_COMMITTED; 2355 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2356 goto FailPath; 2357 } 2358 2359 /* Compute starting and ending PTE and PDE addresses */ 2360 PointerPde = MiAddressToPde(StartingAddress); 2361 PointerPte = MiAddressToPte(StartingAddress); 2362 LastPte = MiAddressToPte(EndingAddress); 2363 2364 /* Make this PDE valid */ 2365 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2366 2367 /* Save protection of the first page */ 2368 if (PointerPte->u.Long != 0) 2369 { 2370 /* Capture the page protection and make the PDE valid */ 2371 OldProtect = MiGetPageProtection(PointerPte); 2372 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2373 } 2374 else 2375 { 2376 /* Grab the old protection from the VAD itself */ 2377 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; 2378 } 2379 2380 /* Loop all the PTEs now */ 2381 while (PointerPte <= LastPte) 2382 { 2383 /* Check if we've crossed a PDE boundary and make the new PDE valid too */ 2384 if (MiIsPteOnPdeBoundary(PointerPte)) 2385 { 2386 PointerPde = MiPteToPde(PointerPte); 2387 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2388 } 2389 2390 /* Capture the PTE and check if it was empty */ 2391 PteContents = *PointerPte; 2392 if (PteContents.u.Long == 0) 2393 { 2394 /* This used to be a zero PTE and it no longer is, so we must add a 2395 reference to the pagetable. */ 2396 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 2397 } 2398 2399 /* Check what kind of PTE we are dealing with */ 2400 if (PteContents.u.Hard.Valid == 1) 2401 { 2402 /* Get the PFN entry */ 2403 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); 2404 2405 /* We don't support this yet */ 2406 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 2407 2408 /* Check if the page should not be accessible at all */ 2409 if ((NewAccessProtection & PAGE_NOACCESS) || 2410 (NewAccessProtection & PAGE_GUARD)) 2411 { 2412 KIRQL OldIrql = MiAcquirePfnLock(); 2413 2414 /* Mark the PTE as transition and change its protection */ 2415 PteContents.u.Hard.Valid = 0; 2416 PteContents.u.Soft.Transition = 1; 2417 PteContents.u.Trans.Protection = ProtectionMask; 2418 /* Decrease PFN share count and write the PTE */ 2419 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); 2420 // FIXME: remove the page from the WS 2421 MI_WRITE_INVALID_PTE(PointerPte, PteContents); 2422 #ifdef CONFIG_SMP 2423 // FIXME: Should invalidate entry in every CPU TLB 2424 ASSERT(KeNumberProcessors == 1); 2425 #endif 2426 KeInvalidateTlbEntry(MiPteToAddress(PointerPte)); 2427 2428 /* We are done for this PTE */ 2429 MiReleasePfnLock(OldIrql); 2430 } 2431 else 2432 { 2433 /* Write the protection mask and write it with a TLB flush */ 2434 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; 2435 MiFlushTbAndCapture(Vad, 2436 PointerPte, 2437 ProtectionMask, 2438 Pfn1, 2439 TRUE); 2440 } 2441 } 2442 else 2443 { 2444 /* We don't support these cases yet */ 2445 ASSERT(PteContents.u.Soft.Prototype == 0); 2446 //ASSERT(PteContents.u.Soft.Transition == 0); 2447 2448 /* The PTE is already demand-zero, just update the protection mask */ 2449 PteContents.u.Soft.Protection = ProtectionMask; 2450 MI_WRITE_INVALID_PTE(PointerPte, PteContents); 2451 ASSERT(PointerPte->u.Long != 0); 2452 } 2453 2454 /* Move to the next PTE */ 2455 PointerPte++; 2456 } 2457 2458 /* Unlock the working set */ 2459 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2460 } 2461 2462 /* Unlock the address space */ 2463 MmUnlockAddressSpace(AddressSpace); 2464 2465 /* Return parameters and success */ 2466 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1; 2467 *BaseAddress = (PVOID)StartingAddress; 2468 *OldAccessProtection = OldProtect; 2469 return STATUS_SUCCESS; 2470 2471 FailPath: 2472 /* Unlock the address space and return the failure code */ 2473 MmUnlockAddressSpace(AddressSpace); 2474 return Status; 2475 } 2476 2477 VOID 2478 NTAPI 2479 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde, 2480 IN PEPROCESS TargetProcess, 2481 IN KIRQL OldIrql) 2482 { 2483 PMMPTE PointerPte; 2484 #if _MI_PAGING_LEVELS >= 3 2485 PMMPPE PointerPpe = MiPdeToPpe(PointerPde); 2486 #if _MI_PAGING_LEVELS == 4 2487 PMMPXE PointerPxe = MiPdeToPxe(PointerPde); 2488 #endif 2489 #endif 2490 2491 // 2492 // Sanity checks. The latter is because we only use this function with the 2493 // PFN lock not held, so it may go away in the future. 2494 // 2495 ASSERT(KeAreAllApcsDisabled() == TRUE); 2496 ASSERT(OldIrql == MM_NOIRQL); 2497 2498 // 2499 // If everything is already valid, there is nothing to do. 2500 // 2501 if ( 2502 #if _MI_PAGING_LEVELS == 4 2503 (PointerPxe->u.Hard.Valid) && 2504 #endif 2505 #if _MI_PAGING_LEVELS >= 3 2506 (PointerPpe->u.Hard.Valid) && 2507 #endif 2508 (PointerPde->u.Hard.Valid)) 2509 { 2510 return; 2511 } 2512 2513 // 2514 // At least something is invalid, so begin by getting the PTE for the PDE itself 2515 // and then lookup each additional level. We must do it in this precise order 2516 // because the pagfault.c code (as well as in Windows) depends that the next 2517 // level up (higher) must be valid when faulting a lower level 2518 // 2519 PointerPte = MiPteToAddress(PointerPde); 2520 do 2521 { 2522 // 2523 // Make sure APCs continued to be disabled 2524 // 2525 ASSERT(KeAreAllApcsDisabled() == TRUE); 2526 2527 #if _MI_PAGING_LEVELS == 4 2528 // 2529 // First, make the PXE valid if needed 2530 // 2531 if (!PointerPxe->u.Hard.Valid) 2532 { 2533 MiMakeSystemAddressValid(PointerPpe, TargetProcess); 2534 ASSERT(PointerPxe->u.Hard.Valid == 1); 2535 } 2536 #endif 2537 2538 #if _MI_PAGING_LEVELS >= 3 2539 // 2540 // Next, the PPE 2541 // 2542 if (!PointerPpe->u.Hard.Valid) 2543 { 2544 MiMakeSystemAddressValid(PointerPde, TargetProcess); 2545 ASSERT(PointerPpe->u.Hard.Valid == 1); 2546 } 2547 #endif 2548 2549 // 2550 // And finally, make the PDE itself valid. 2551 // 2552 MiMakeSystemAddressValid(PointerPte, TargetProcess); 2553 2554 /* Do not increment Page table refcount here for the PDE, this must be managed by caller */ 2555 2556 // 2557 // This should've worked the first time so the loop is really just for 2558 // show -- ASSERT that we're actually NOT going to be looping. 2559 // 2560 ASSERT(PointerPde->u.Hard.Valid == 1); 2561 } while ( 2562 #if _MI_PAGING_LEVELS == 4 2563 !PointerPxe->u.Hard.Valid || 2564 #endif 2565 #if _MI_PAGING_LEVELS >= 3 2566 !PointerPpe->u.Hard.Valid || 2567 #endif 2568 !PointerPde->u.Hard.Valid); 2569 } 2570 2571 VOID 2572 NTAPI 2573 MiProcessValidPteList(IN PMMPTE *ValidPteList, 2574 IN ULONG Count) 2575 { 2576 KIRQL OldIrql; 2577 ULONG i; 2578 MMPTE TempPte; 2579 PFN_NUMBER PageFrameIndex; 2580 PMMPFN Pfn1, Pfn2; 2581 2582 // 2583 // Acquire the PFN lock and loop all the PTEs in the list 2584 // 2585 OldIrql = MiAcquirePfnLock(); 2586 for (i = 0; i != Count; i++) 2587 { 2588 // 2589 // The PTE must currently be valid 2590 // 2591 TempPte = *ValidPteList[i]; 2592 ASSERT(TempPte.u.Hard.Valid == 1); 2593 2594 // 2595 // Get the PFN entry for the page itself, and then for its page table 2596 // 2597 PageFrameIndex = PFN_FROM_PTE(&TempPte); 2598 Pfn1 = MiGetPfnEntry(PageFrameIndex); 2599 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame); 2600 2601 // 2602 // Decrement the share count on the page table, and then on the page 2603 // itself 2604 // 2605 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame); 2606 MI_SET_PFN_DELETED(Pfn1); 2607 MiDecrementShareCount(Pfn1, PageFrameIndex); 2608 2609 // 2610 // Make the page decommitted 2611 // 2612 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte); 2613 } 2614 2615 // 2616 // All the PTEs have been dereferenced and made invalid, flush the TLB now 2617 // and then release the PFN lock 2618 // 2619 KeFlushCurrentTb(); 2620 MiReleasePfnLock(OldIrql); 2621 } 2622 2623 ULONG 2624 NTAPI 2625 MiDecommitPages(IN PVOID StartingAddress, 2626 IN PMMPTE EndingPte, 2627 IN PEPROCESS Process, 2628 IN PMMVAD Vad) 2629 { 2630 PMMPTE PointerPte, CommitPte = NULL; 2631 PMMPDE PointerPde; 2632 ULONG CommitReduction = 0; 2633 PMMPTE ValidPteList[256]; 2634 ULONG PteCount = 0; 2635 PMMPFN Pfn1; 2636 MMPTE PteContents; 2637 PETHREAD CurrentThread = PsGetCurrentThread(); 2638 2639 // 2640 // Get the PTE and PTE for the address, and lock the working set 2641 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the 2642 // commited range ends so that we can do the right accounting. 2643 // 2644 PointerPde = MiAddressToPde(StartingAddress); 2645 PointerPte = MiAddressToPte(StartingAddress); 2646 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT); 2647 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 2648 2649 // 2650 // Make the PDE valid, and now loop through each page's worth of data 2651 // 2652 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2653 while (PointerPte <= EndingPte) 2654 { 2655 // 2656 // Check if we've crossed a PDE boundary 2657 // 2658 if (MiIsPteOnPdeBoundary(PointerPte)) 2659 { 2660 // 2661 // Get the new PDE and flush the valid PTEs we had built up until 2662 // now. This helps reduce the amount of TLB flushing we have to do. 2663 // Note that Windows does a much better job using timestamps and 2664 // such, and does not flush the entire TLB all the time, but right 2665 // now we have bigger problems to worry about than TLB flushing. 2666 // 2667 PointerPde = MiAddressToPde(StartingAddress); 2668 if (PteCount) 2669 { 2670 MiProcessValidPteList(ValidPteList, PteCount); 2671 PteCount = 0; 2672 } 2673 2674 // 2675 // Make this PDE valid 2676 // 2677 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2678 } 2679 2680 // 2681 // Read this PTE. It might be active or still demand-zero. 2682 // 2683 PteContents = *PointerPte; 2684 if (PteContents.u.Long) 2685 { 2686 // 2687 // The PTE is active. It might be valid and in a working set, or 2688 // it might be a prototype PTE or paged out or even in transition. 2689 // 2690 if (PointerPte->u.Long == MmDecommittedPte.u.Long) 2691 { 2692 // 2693 // It's already decommited, so there's nothing for us to do here 2694 // 2695 CommitReduction++; 2696 } 2697 else 2698 { 2699 // 2700 // Remove it from the counters, and check if it was valid or not 2701 // 2702 //Process->NumberOfPrivatePages--; 2703 if (PteContents.u.Hard.Valid) 2704 { 2705 // 2706 // It's valid. At this point make sure that it is not a ROS 2707 // PFN. Also, we don't support ProtoPTEs in this code path. 2708 // 2709 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber); 2710 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE); 2711 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE); 2712 2713 // 2714 // Flush any pending PTEs that we had not yet flushed, if our 2715 // list has gotten too big, then add this PTE to the flush list. 2716 // 2717 if (PteCount == 256) 2718 { 2719 MiProcessValidPteList(ValidPteList, PteCount); 2720 PteCount = 0; 2721 } 2722 ValidPteList[PteCount++] = PointerPte; 2723 } 2724 else 2725 { 2726 // 2727 // We do not support any of these other scenarios at the moment 2728 // 2729 ASSERT(PteContents.u.Soft.Prototype == 0); 2730 ASSERT(PteContents.u.Soft.Transition == 0); 2731 ASSERT(PteContents.u.Soft.PageFileHigh == 0); 2732 2733 // 2734 // So the only other possibility is that it is still a demand 2735 // zero PTE, in which case we undo the accounting we did 2736 // earlier and simply make the page decommitted. 2737 // 2738 //Process->NumberOfPrivatePages++; 2739 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 2740 } 2741 } 2742 } 2743 else 2744 { 2745 // 2746 // This used to be a zero PTE and it no longer is, so we must add a 2747 // reference to the pagetable. 2748 // 2749 MiIncrementPageTableReferences(StartingAddress); 2750 2751 // 2752 // Next, we account for decommitted PTEs and make the PTE as such 2753 // 2754 if (PointerPte > CommitPte) CommitReduction++; 2755 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 2756 } 2757 2758 // 2759 // Move to the next PTE and the next address 2760 // 2761 PointerPte++; 2762 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE); 2763 } 2764 2765 // 2766 // Flush any dangling PTEs from the loop in the last page table, and then 2767 // release the working set and return the commit reduction accounting. 2768 // 2769 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount); 2770 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 2771 return CommitReduction; 2772 } 2773 2774 /* PUBLIC FUNCTIONS ***********************************************************/ 2775 2776 /* 2777 * @unimplemented 2778 */ 2779 PVOID 2780 NTAPI 2781 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress) 2782 { 2783 UNIMPLEMENTED; 2784 return 0; 2785 } 2786 2787 /* 2788 * @unimplemented 2789 */ 2790 PVOID 2791 NTAPI 2792 MmSecureVirtualMemory(IN PVOID Address, 2793 IN SIZE_T Length, 2794 IN ULONG Mode) 2795 { 2796 static ULONG Warn; if (!Warn++) UNIMPLEMENTED; 2797 return Address; 2798 } 2799 2800 /* 2801 * @unimplemented 2802 */ 2803 VOID 2804 NTAPI 2805 MmUnsecureVirtualMemory(IN PVOID SecureMem) 2806 { 2807 static ULONG Warn; if (!Warn++) UNIMPLEMENTED; 2808 } 2809 2810 /* SYSTEM CALLS ***************************************************************/ 2811 2812 NTSTATUS 2813 NTAPI 2814 NtReadVirtualMemory(IN HANDLE ProcessHandle, 2815 IN PVOID BaseAddress, 2816 OUT PVOID Buffer, 2817 IN SIZE_T NumberOfBytesToRead, 2818 OUT PSIZE_T NumberOfBytesRead OPTIONAL) 2819 { 2820 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2821 PEPROCESS Process; 2822 NTSTATUS Status = STATUS_SUCCESS; 2823 SIZE_T BytesRead = 0; 2824 PAGED_CODE(); 2825 2826 // 2827 // Check if we came from user mode 2828 // 2829 if (PreviousMode != KernelMode) 2830 { 2831 // 2832 // Validate the read addresses 2833 // 2834 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) || 2835 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) || 2836 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) || 2837 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress)) 2838 { 2839 // 2840 // Don't allow to write into kernel space 2841 // 2842 return STATUS_ACCESS_VIOLATION; 2843 } 2844 2845 // 2846 // Enter SEH for probe 2847 // 2848 _SEH2_TRY 2849 { 2850 // 2851 // Probe the output value 2852 // 2853 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead); 2854 } 2855 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2856 { 2857 // 2858 // Get exception code 2859 // 2860 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2861 } 2862 _SEH2_END; 2863 } 2864 2865 // 2866 // Don't do zero-byte transfers 2867 // 2868 if (NumberOfBytesToRead) 2869 { 2870 // 2871 // Reference the process 2872 // 2873 Status = ObReferenceObjectByHandle(ProcessHandle, 2874 PROCESS_VM_READ, 2875 PsProcessType, 2876 PreviousMode, 2877 (PVOID*)(&Process), 2878 NULL); 2879 if (NT_SUCCESS(Status)) 2880 { 2881 // 2882 // Do the copy 2883 // 2884 Status = MmCopyVirtualMemory(Process, 2885 BaseAddress, 2886 PsGetCurrentProcess(), 2887 Buffer, 2888 NumberOfBytesToRead, 2889 PreviousMode, 2890 &BytesRead); 2891 2892 // 2893 // Dereference the process 2894 // 2895 ObDereferenceObject(Process); 2896 } 2897 } 2898 2899 // 2900 // Check if the caller sent this parameter 2901 // 2902 if (NumberOfBytesRead) 2903 { 2904 // 2905 // Enter SEH to guard write 2906 // 2907 _SEH2_TRY 2908 { 2909 // 2910 // Return the number of bytes read 2911 // 2912 *NumberOfBytesRead = BytesRead; 2913 } 2914 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2915 { 2916 } 2917 _SEH2_END; 2918 } 2919 2920 // 2921 // Return status 2922 // 2923 return Status; 2924 } 2925 2926 NTSTATUS 2927 NTAPI 2928 NtWriteVirtualMemory(IN HANDLE ProcessHandle, 2929 IN PVOID BaseAddress, 2930 IN PVOID Buffer, 2931 IN SIZE_T NumberOfBytesToWrite, 2932 OUT PSIZE_T NumberOfBytesWritten OPTIONAL) 2933 { 2934 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2935 PEPROCESS Process; 2936 NTSTATUS Status = STATUS_SUCCESS; 2937 SIZE_T BytesWritten = 0; 2938 PAGED_CODE(); 2939 2940 // 2941 // Check if we came from user mode 2942 // 2943 if (PreviousMode != KernelMode) 2944 { 2945 // 2946 // Validate the read addresses 2947 // 2948 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) || 2949 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) || 2950 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) || 2951 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress)) 2952 { 2953 // 2954 // Don't allow to write into kernel space 2955 // 2956 return STATUS_ACCESS_VIOLATION; 2957 } 2958 2959 // 2960 // Enter SEH for probe 2961 // 2962 _SEH2_TRY 2963 { 2964 // 2965 // Probe the output value 2966 // 2967 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten); 2968 } 2969 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2970 { 2971 // 2972 // Get exception code 2973 // 2974 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2975 } 2976 _SEH2_END; 2977 } 2978 2979 // 2980 // Don't do zero-byte transfers 2981 // 2982 if (NumberOfBytesToWrite) 2983 { 2984 // 2985 // Reference the process 2986 // 2987 Status = ObReferenceObjectByHandle(ProcessHandle, 2988 PROCESS_VM_WRITE, 2989 PsProcessType, 2990 PreviousMode, 2991 (PVOID*)&Process, 2992 NULL); 2993 if (NT_SUCCESS(Status)) 2994 { 2995 // 2996 // Do the copy 2997 // 2998 Status = MmCopyVirtualMemory(PsGetCurrentProcess(), 2999 Buffer, 3000 Process, 3001 BaseAddress, 3002 NumberOfBytesToWrite, 3003 PreviousMode, 3004 &BytesWritten); 3005 3006 // 3007 // Dereference the process 3008 // 3009 ObDereferenceObject(Process); 3010 } 3011 } 3012 3013 // 3014 // Check if the caller sent this parameter 3015 // 3016 if (NumberOfBytesWritten) 3017 { 3018 // 3019 // Enter SEH to guard write 3020 // 3021 _SEH2_TRY 3022 { 3023 // 3024 // Return the number of bytes written 3025 // 3026 *NumberOfBytesWritten = BytesWritten; 3027 } 3028 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3029 { 3030 } 3031 _SEH2_END; 3032 } 3033 3034 // 3035 // Return status 3036 // 3037 return Status; 3038 } 3039 3040 NTSTATUS 3041 NTAPI 3042 NtFlushInstructionCache(_In_ HANDLE ProcessHandle, 3043 _In_opt_ PVOID BaseAddress, 3044 _In_ SIZE_T FlushSize) 3045 { 3046 KAPC_STATE ApcState; 3047 PKPROCESS Process; 3048 NTSTATUS Status; 3049 PAGED_CODE(); 3050 3051 /* Is a base address given? */ 3052 if (BaseAddress != NULL) 3053 { 3054 /* If the requested size is 0, there is nothing to do */ 3055 if (FlushSize == 0) 3056 { 3057 return STATUS_SUCCESS; 3058 } 3059 3060 /* Is this a user mode call? */ 3061 if (ExGetPreviousMode() != KernelMode) 3062 { 3063 /* Make sure the base address is in user space */ 3064 if (BaseAddress > MmHighestUserAddress) 3065 { 3066 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress); 3067 return STATUS_ACCESS_VIOLATION; 3068 } 3069 } 3070 } 3071 3072 /* Is another process requested? */ 3073 if (ProcessHandle != NtCurrentProcess()) 3074 { 3075 /* Reference the process */ 3076 Status = ObReferenceObjectByHandle(ProcessHandle, 3077 PROCESS_VM_WRITE, 3078 PsProcessType, 3079 ExGetPreviousMode(), 3080 (PVOID*)&Process, 3081 NULL); 3082 if (!NT_SUCCESS(Status)) 3083 { 3084 DPRINT1("Failed to reference the process %p\n", ProcessHandle); 3085 return Status; 3086 } 3087 3088 /* Attach to the process */ 3089 KeStackAttachProcess(Process, &ApcState); 3090 } 3091 3092 /* Forward to Ke */ 3093 KeSweepICache(BaseAddress, FlushSize); 3094 3095 /* Check if we attached */ 3096 if (ProcessHandle != NtCurrentProcess()) 3097 { 3098 /* Detach from the process and dereference it */ 3099 KeUnstackDetachProcess(&ApcState); 3100 ObDereferenceObject(Process); 3101 } 3102 3103 /* All done, return to caller */ 3104 return STATUS_SUCCESS; 3105 } 3106 3107 NTSTATUS 3108 NTAPI 3109 NtProtectVirtualMemory(IN HANDLE ProcessHandle, 3110 IN OUT PVOID *UnsafeBaseAddress, 3111 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect, 3112 IN ULONG NewAccessProtection, 3113 OUT PULONG UnsafeOldAccessProtection) 3114 { 3115 PEPROCESS Process; 3116 ULONG OldAccessProtection; 3117 ULONG Protection; 3118 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3119 PVOID BaseAddress = NULL; 3120 SIZE_T NumberOfBytesToProtect = 0; 3121 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3122 NTSTATUS Status; 3123 BOOLEAN Attached = FALSE; 3124 KAPC_STATE ApcState; 3125 PAGED_CODE(); 3126 3127 // 3128 // Check for valid protection flags 3129 // 3130 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE); 3131 if (Protection != PAGE_NOACCESS && 3132 Protection != PAGE_READONLY && 3133 Protection != PAGE_READWRITE && 3134 Protection != PAGE_WRITECOPY && 3135 Protection != PAGE_EXECUTE && 3136 Protection != PAGE_EXECUTE_READ && 3137 Protection != PAGE_EXECUTE_READWRITE && 3138 Protection != PAGE_EXECUTE_WRITECOPY) 3139 { 3140 // 3141 // Fail 3142 // 3143 return STATUS_INVALID_PAGE_PROTECTION; 3144 } 3145 3146 // 3147 // Check if we came from user mode 3148 // 3149 if (PreviousMode != KernelMode) 3150 { 3151 // 3152 // Enter SEH for probing 3153 // 3154 _SEH2_TRY 3155 { 3156 // 3157 // Validate all outputs 3158 // 3159 ProbeForWritePointer(UnsafeBaseAddress); 3160 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect); 3161 ProbeForWriteUlong(UnsafeOldAccessProtection); 3162 3163 // 3164 // Capture them 3165 // 3166 BaseAddress = *UnsafeBaseAddress; 3167 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 3168 } 3169 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3170 { 3171 // 3172 // Get exception code 3173 // 3174 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3175 } 3176 _SEH2_END; 3177 } 3178 else 3179 { 3180 // 3181 // Capture directly 3182 // 3183 BaseAddress = *UnsafeBaseAddress; 3184 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 3185 } 3186 3187 // 3188 // Catch illegal base address 3189 // 3190 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 3191 3192 // 3193 // Catch illegal region size 3194 // 3195 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect) 3196 { 3197 // 3198 // Fail 3199 // 3200 return STATUS_INVALID_PARAMETER_3; 3201 } 3202 3203 // 3204 // 0 is also illegal 3205 // 3206 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3; 3207 3208 // 3209 // Get a reference to the process 3210 // 3211 Status = ObReferenceObjectByHandle(ProcessHandle, 3212 PROCESS_VM_OPERATION, 3213 PsProcessType, 3214 PreviousMode, 3215 (PVOID*)(&Process), 3216 NULL); 3217 if (!NT_SUCCESS(Status)) return Status; 3218 3219 // 3220 // Check if we should attach 3221 // 3222 if (CurrentProcess != Process) 3223 { 3224 // 3225 // Do it 3226 // 3227 KeStackAttachProcess(&Process->Pcb, &ApcState); 3228 Attached = TRUE; 3229 } 3230 3231 // 3232 // Do the actual work 3233 // 3234 Status = MiProtectVirtualMemory(Process, 3235 &BaseAddress, 3236 &NumberOfBytesToProtect, 3237 NewAccessProtection, 3238 &OldAccessProtection); 3239 3240 // 3241 // Detach if needed 3242 // 3243 if (Attached) KeUnstackDetachProcess(&ApcState); 3244 3245 // 3246 // Release reference 3247 // 3248 ObDereferenceObject(Process); 3249 3250 // 3251 // Enter SEH to return data 3252 // 3253 _SEH2_TRY 3254 { 3255 // 3256 // Return data to user 3257 // 3258 *UnsafeOldAccessProtection = OldAccessProtection; 3259 *UnsafeBaseAddress = BaseAddress; 3260 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect; 3261 } 3262 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3263 { 3264 } 3265 _SEH2_END; 3266 3267 // 3268 // Return status 3269 // 3270 return Status; 3271 } 3272 3273 FORCEINLINE 3274 BOOLEAN 3275 MI_IS_LOCKED_VA( 3276 PMMPFN Pfn1, 3277 ULONG LockType) 3278 { 3279 // HACK until we have proper WSLIST support 3280 PMMWSLE Wsle = &Pfn1->Wsle; 3281 3282 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs)) 3283 return TRUE; 3284 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory)) 3285 return TRUE; 3286 3287 return FALSE; 3288 } 3289 3290 FORCEINLINE 3291 VOID 3292 MI_LOCK_VA( 3293 PMMPFN Pfn1, 3294 ULONG LockType) 3295 { 3296 // HACK until we have proper WSLIST support 3297 PMMWSLE Wsle = &Pfn1->Wsle; 3298 3299 if (!Wsle->u1.e1.LockedInWs && 3300 !Wsle->u1.e1.LockedInMemory) 3301 { 3302 MiReferenceProbedPageAndBumpLockCount(Pfn1); 3303 } 3304 3305 if (LockType & MAP_PROCESS) 3306 Wsle->u1.e1.LockedInWs = 1; 3307 if (LockType & MAP_SYSTEM) 3308 Wsle->u1.e1.LockedInMemory = 1; 3309 } 3310 3311 FORCEINLINE 3312 VOID 3313 MI_UNLOCK_VA( 3314 PMMPFN Pfn1, 3315 ULONG LockType) 3316 { 3317 // HACK until we have proper WSLIST support 3318 PMMWSLE Wsle = &Pfn1->Wsle; 3319 3320 if (LockType & MAP_PROCESS) 3321 Wsle->u1.e1.LockedInWs = 0; 3322 if (LockType & MAP_SYSTEM) 3323 Wsle->u1.e1.LockedInMemory = 0; 3324 3325 if (!Wsle->u1.e1.LockedInWs && 3326 !Wsle->u1.e1.LockedInMemory) 3327 { 3328 MiDereferencePfnAndDropLockCount(Pfn1); 3329 } 3330 } 3331 3332 static 3333 NTSTATUS 3334 MiCheckVadsForLockOperation( 3335 _Inout_ PVOID *BaseAddress, 3336 _Inout_ PSIZE_T RegionSize, 3337 _Inout_ PVOID *EndAddress) 3338 3339 { 3340 PMMVAD Vad; 3341 PVOID CurrentVa; 3342 3343 /* Get the base address and align the start address */ 3344 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize; 3345 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE); 3346 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE); 3347 3348 /* First loop and check all VADs */ 3349 CurrentVa = *BaseAddress; 3350 while (CurrentVa < *EndAddress) 3351 { 3352 /* Get VAD */ 3353 Vad = MiLocateAddress(CurrentVa); 3354 if (Vad == NULL) 3355 { 3356 /// FIXME: this might be a memory area for a section view... 3357 return STATUS_ACCESS_VIOLATION; 3358 } 3359 3360 /* Check VAD type */ 3361 if ((Vad->u.VadFlags.VadType != VadNone) && 3362 (Vad->u.VadFlags.VadType != VadImageMap) && 3363 (Vad->u.VadFlags.VadType != VadWriteWatch)) 3364 { 3365 *EndAddress = CurrentVa; 3366 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress; 3367 return STATUS_INCOMPATIBLE_FILE_MAP; 3368 } 3369 3370 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT); 3371 } 3372 3373 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress; 3374 return STATUS_SUCCESS; 3375 } 3376 3377 static 3378 NTSTATUS 3379 MiLockVirtualMemory( 3380 IN OUT PVOID *BaseAddress, 3381 IN OUT PSIZE_T RegionSize, 3382 IN ULONG MapType) 3383 { 3384 PEPROCESS CurrentProcess; 3385 PMMSUPPORT AddressSpace; 3386 PVOID CurrentVa, EndAddress; 3387 PMMPTE PointerPte, LastPte; 3388 PMMPDE PointerPde; 3389 #if (_MI_PAGING_LEVELS >= 3) 3390 PMMPDE PointerPpe; 3391 #endif 3392 #if (_MI_PAGING_LEVELS == 4) 3393 PMMPDE PointerPxe; 3394 #endif 3395 PMMPFN Pfn1; 3396 NTSTATUS Status, TempStatus; 3397 3398 /* Lock the address space */ 3399 AddressSpace = MmGetCurrentAddressSpace(); 3400 MmLockAddressSpace(AddressSpace); 3401 3402 /* Make sure we still have an address space */ 3403 CurrentProcess = PsGetCurrentProcess(); 3404 if (CurrentProcess->VmDeleted) 3405 { 3406 Status = STATUS_PROCESS_IS_TERMINATING; 3407 goto Cleanup; 3408 } 3409 3410 /* Check the VADs in the requested range */ 3411 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress); 3412 if (!NT_SUCCESS(Status)) 3413 { 3414 goto Cleanup; 3415 } 3416 3417 /* Enter SEH for probing */ 3418 _SEH2_TRY 3419 { 3420 /* Loop all pages and probe them */ 3421 CurrentVa = *BaseAddress; 3422 while (CurrentVa < EndAddress) 3423 { 3424 (void)(*(volatile CHAR*)CurrentVa); 3425 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE; 3426 } 3427 } 3428 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3429 { 3430 Status = _SEH2_GetExceptionCode(); 3431 goto Cleanup; 3432 } 3433 _SEH2_END; 3434 3435 /* All pages were accessible, since we hold the address space lock, nothing 3436 can be de-committed. Assume success for now. */ 3437 Status = STATUS_SUCCESS; 3438 3439 /* Get the PTE and PDE */ 3440 PointerPte = MiAddressToPte(*BaseAddress); 3441 PointerPde = MiAddressToPde(*BaseAddress); 3442 #if (_MI_PAGING_LEVELS >= 3) 3443 PointerPpe = MiAddressToPpe(*BaseAddress); 3444 #endif 3445 #if (_MI_PAGING_LEVELS == 4) 3446 PointerPxe = MiAddressToPxe(*BaseAddress); 3447 #endif 3448 3449 /* Get the last PTE */ 3450 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1)); 3451 3452 /* Lock the process working set */ 3453 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3454 3455 /* Loop the pages */ 3456 do 3457 { 3458 /* Check for a page that is not accessible */ 3459 while ( 3460 #if (_MI_PAGING_LEVELS == 4) 3461 (PointerPxe->u.Hard.Valid == 0) || 3462 #endif 3463 #if (_MI_PAGING_LEVELS >= 3) 3464 (PointerPpe->u.Hard.Valid == 0) || 3465 #endif 3466 (PointerPde->u.Hard.Valid == 0) || 3467 (PointerPte->u.Hard.Valid == 0)) 3468 { 3469 /* Release process working set */ 3470 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3471 3472 /* Access the page */ 3473 CurrentVa = MiPteToAddress(PointerPte); 3474 3475 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked 3476 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL); 3477 if (!NT_SUCCESS(TempStatus)) 3478 { 3479 // This should only happen, when remote backing storage is not accessible 3480 ASSERT(FALSE); 3481 Status = TempStatus; 3482 goto Cleanup; 3483 } 3484 3485 /* Lock the process working set */ 3486 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3487 } 3488 3489 /* Get the PFN */ 3490 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3491 ASSERT(Pfn1 != NULL); 3492 3493 /* Check the previous lock status */ 3494 if (MI_IS_LOCKED_VA(Pfn1, MapType)) 3495 { 3496 Status = STATUS_WAS_LOCKED; 3497 } 3498 3499 /* Lock it */ 3500 MI_LOCK_VA(Pfn1, MapType); 3501 3502 /* Go to the next PTE */ 3503 PointerPte++; 3504 3505 /* Check if we're on a PDE boundary */ 3506 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3507 #if (_MI_PAGING_LEVELS >= 3) 3508 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3509 #endif 3510 #if (_MI_PAGING_LEVELS == 4) 3511 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3512 #endif 3513 } while (PointerPte <= LastPte); 3514 3515 /* Release process working set */ 3516 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3517 3518 Cleanup: 3519 /* Unlock address space */ 3520 MmUnlockAddressSpace(AddressSpace); 3521 3522 return Status; 3523 } 3524 3525 NTSTATUS 3526 NTAPI 3527 NtLockVirtualMemory(IN HANDLE ProcessHandle, 3528 IN OUT PVOID *BaseAddress, 3529 IN OUT PSIZE_T NumberOfBytesToLock, 3530 IN ULONG MapType) 3531 { 3532 PEPROCESS Process; 3533 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3534 NTSTATUS Status; 3535 BOOLEAN Attached = FALSE; 3536 KAPC_STATE ApcState; 3537 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3538 PVOID CapturedBaseAddress; 3539 SIZE_T CapturedBytesToLock; 3540 PAGED_CODE(); 3541 3542 // 3543 // Validate flags 3544 // 3545 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 3546 { 3547 // 3548 // Invalid set of flags 3549 // 3550 return STATUS_INVALID_PARAMETER; 3551 } 3552 3553 // 3554 // At least one flag must be specified 3555 // 3556 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 3557 { 3558 // 3559 // No flag given 3560 // 3561 return STATUS_INVALID_PARAMETER; 3562 } 3563 3564 // 3565 // Enter SEH for probing 3566 // 3567 _SEH2_TRY 3568 { 3569 // 3570 // Validate output data 3571 // 3572 ProbeForWritePointer(BaseAddress); 3573 ProbeForWriteSize_t(NumberOfBytesToLock); 3574 3575 // 3576 // Capture it 3577 // 3578 CapturedBaseAddress = *BaseAddress; 3579 CapturedBytesToLock = *NumberOfBytesToLock; 3580 } 3581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3582 { 3583 // 3584 // Get exception code 3585 // 3586 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3587 } 3588 _SEH2_END; 3589 3590 // 3591 // Catch illegal base address 3592 // 3593 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 3594 3595 // 3596 // Catch illegal region size 3597 // 3598 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock) 3599 { 3600 // 3601 // Fail 3602 // 3603 return STATUS_INVALID_PARAMETER; 3604 } 3605 3606 // 3607 // 0 is also illegal 3608 // 3609 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER; 3610 3611 // 3612 // Get a reference to the process 3613 // 3614 Status = ObReferenceObjectByHandle(ProcessHandle, 3615 PROCESS_VM_OPERATION, 3616 PsProcessType, 3617 PreviousMode, 3618 (PVOID*)(&Process), 3619 NULL); 3620 if (!NT_SUCCESS(Status)) return Status; 3621 3622 // 3623 // Check if this is is system-mapped 3624 // 3625 if (MapType & MAP_SYSTEM) 3626 { 3627 // 3628 // Check for required privilege 3629 // 3630 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 3631 { 3632 // 3633 // Fail: Don't have it 3634 // 3635 ObDereferenceObject(Process); 3636 return STATUS_PRIVILEGE_NOT_HELD; 3637 } 3638 } 3639 3640 // 3641 // Check if we should attach 3642 // 3643 if (CurrentProcess != Process) 3644 { 3645 // 3646 // Do it 3647 // 3648 KeStackAttachProcess(&Process->Pcb, &ApcState); 3649 Attached = TRUE; 3650 } 3651 3652 // 3653 // Call the internal function 3654 // 3655 Status = MiLockVirtualMemory(&CapturedBaseAddress, 3656 &CapturedBytesToLock, 3657 MapType); 3658 3659 // 3660 // Detach if needed 3661 // 3662 if (Attached) KeUnstackDetachProcess(&ApcState); 3663 3664 // 3665 // Release reference 3666 // 3667 ObDereferenceObject(Process); 3668 3669 // 3670 // Enter SEH to return data 3671 // 3672 _SEH2_TRY 3673 { 3674 // 3675 // Return data to user 3676 // 3677 *BaseAddress = CapturedBaseAddress; 3678 *NumberOfBytesToLock = CapturedBytesToLock; 3679 } 3680 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3681 { 3682 // 3683 // Get exception code 3684 // 3685 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3686 } 3687 _SEH2_END; 3688 3689 // 3690 // Return status 3691 // 3692 return Status; 3693 } 3694 3695 3696 static 3697 NTSTATUS 3698 MiUnlockVirtualMemory( 3699 IN OUT PVOID *BaseAddress, 3700 IN OUT PSIZE_T RegionSize, 3701 IN ULONG MapType) 3702 { 3703 PEPROCESS CurrentProcess; 3704 PMMSUPPORT AddressSpace; 3705 PVOID EndAddress; 3706 PMMPTE PointerPte, LastPte; 3707 PMMPDE PointerPde; 3708 #if (_MI_PAGING_LEVELS >= 3) 3709 PMMPDE PointerPpe; 3710 #endif 3711 #if (_MI_PAGING_LEVELS == 4) 3712 PMMPDE PointerPxe; 3713 #endif 3714 PMMPFN Pfn1; 3715 NTSTATUS Status; 3716 3717 /* Lock the address space */ 3718 AddressSpace = MmGetCurrentAddressSpace(); 3719 MmLockAddressSpace(AddressSpace); 3720 3721 /* Make sure we still have an address space */ 3722 CurrentProcess = PsGetCurrentProcess(); 3723 if (CurrentProcess->VmDeleted) 3724 { 3725 Status = STATUS_PROCESS_IS_TERMINATING; 3726 goto Cleanup; 3727 } 3728 3729 /* Check the VADs in the requested range */ 3730 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress); 3731 3732 /* Note: only bail out, if we hit an area without a VAD. If we hit an 3733 incompatible VAD we continue, like Windows does */ 3734 if (Status == STATUS_ACCESS_VIOLATION) 3735 { 3736 Status = STATUS_NOT_LOCKED; 3737 goto Cleanup; 3738 } 3739 3740 /* Get the PTE and PDE */ 3741 PointerPte = MiAddressToPte(*BaseAddress); 3742 PointerPde = MiAddressToPde(*BaseAddress); 3743 #if (_MI_PAGING_LEVELS >= 3) 3744 PointerPpe = MiAddressToPpe(*BaseAddress); 3745 #endif 3746 #if (_MI_PAGING_LEVELS == 4) 3747 PointerPxe = MiAddressToPxe(*BaseAddress); 3748 #endif 3749 3750 /* Get the last PTE */ 3751 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1)); 3752 3753 /* Lock the process working set */ 3754 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3755 3756 /* Loop the pages */ 3757 do 3758 { 3759 /* Check for a page that is not present */ 3760 if ( 3761 #if (_MI_PAGING_LEVELS == 4) 3762 (PointerPxe->u.Hard.Valid == 0) || 3763 #endif 3764 #if (_MI_PAGING_LEVELS >= 3) 3765 (PointerPpe->u.Hard.Valid == 0) || 3766 #endif 3767 (PointerPde->u.Hard.Valid == 0) || 3768 (PointerPte->u.Hard.Valid == 0)) 3769 { 3770 /* Remember it, but keep going */ 3771 Status = STATUS_NOT_LOCKED; 3772 } 3773 else 3774 { 3775 /* Get the PFN */ 3776 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3777 ASSERT(Pfn1 != NULL); 3778 3779 /* Check if all of the requested locks are present */ 3780 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) || 3781 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS))) 3782 { 3783 /* Remember it, but keep going */ 3784 Status = STATUS_NOT_LOCKED; 3785 3786 /* Check if no lock is present */ 3787 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM)) 3788 { 3789 DPRINT1("FIXME: Should remove the page from WS\n"); 3790 } 3791 } 3792 } 3793 3794 /* Go to the next PTE */ 3795 PointerPte++; 3796 3797 /* Check if we're on a PDE boundary */ 3798 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3799 #if (_MI_PAGING_LEVELS >= 3) 3800 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3801 #endif 3802 #if (_MI_PAGING_LEVELS == 4) 3803 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3804 #endif 3805 } while (PointerPte <= LastPte); 3806 3807 /* Check if we hit a page that was not locked */ 3808 if (Status == STATUS_NOT_LOCKED) 3809 { 3810 goto CleanupWithWsLock; 3811 } 3812 3813 /* All pages in the region were locked, so unlock them all */ 3814 3815 /* Get the PTE and PDE */ 3816 PointerPte = MiAddressToPte(*BaseAddress); 3817 PointerPde = MiAddressToPde(*BaseAddress); 3818 #if (_MI_PAGING_LEVELS >= 3) 3819 PointerPpe = MiAddressToPpe(*BaseAddress); 3820 #endif 3821 #if (_MI_PAGING_LEVELS == 4) 3822 PointerPxe = MiAddressToPxe(*BaseAddress); 3823 #endif 3824 3825 /* Loop the pages */ 3826 do 3827 { 3828 /* Unlock it */ 3829 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3830 MI_UNLOCK_VA(Pfn1, MapType); 3831 3832 /* Go to the next PTE */ 3833 PointerPte++; 3834 3835 /* Check if we're on a PDE boundary */ 3836 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3837 #if (_MI_PAGING_LEVELS >= 3) 3838 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3839 #endif 3840 #if (_MI_PAGING_LEVELS == 4) 3841 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3842 #endif 3843 } while (PointerPte <= LastPte); 3844 3845 /* Everything is done */ 3846 Status = STATUS_SUCCESS; 3847 3848 CleanupWithWsLock: 3849 3850 /* Release process working set */ 3851 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3852 3853 Cleanup: 3854 /* Unlock address space */ 3855 MmUnlockAddressSpace(AddressSpace); 3856 3857 return Status; 3858 } 3859 3860 3861 NTSTATUS 3862 NTAPI 3863 NtUnlockVirtualMemory(IN HANDLE ProcessHandle, 3864 IN OUT PVOID *BaseAddress, 3865 IN OUT PSIZE_T NumberOfBytesToUnlock, 3866 IN ULONG MapType) 3867 { 3868 PEPROCESS Process; 3869 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3870 NTSTATUS Status; 3871 BOOLEAN Attached = FALSE; 3872 KAPC_STATE ApcState; 3873 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3874 PVOID CapturedBaseAddress; 3875 SIZE_T CapturedBytesToUnlock; 3876 PAGED_CODE(); 3877 3878 // 3879 // Validate flags 3880 // 3881 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 3882 { 3883 // 3884 // Invalid set of flags 3885 // 3886 return STATUS_INVALID_PARAMETER; 3887 } 3888 3889 // 3890 // At least one flag must be specified 3891 // 3892 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 3893 { 3894 // 3895 // No flag given 3896 // 3897 return STATUS_INVALID_PARAMETER; 3898 } 3899 3900 // 3901 // Enter SEH for probing 3902 // 3903 _SEH2_TRY 3904 { 3905 // 3906 // Validate output data 3907 // 3908 ProbeForWritePointer(BaseAddress); 3909 ProbeForWriteSize_t(NumberOfBytesToUnlock); 3910 3911 // 3912 // Capture it 3913 // 3914 CapturedBaseAddress = *BaseAddress; 3915 CapturedBytesToUnlock = *NumberOfBytesToUnlock; 3916 } 3917 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3918 { 3919 // 3920 // Get exception code 3921 // 3922 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3923 } 3924 _SEH2_END; 3925 3926 // 3927 // Catch illegal base address 3928 // 3929 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 3930 3931 // 3932 // Catch illegal region size 3933 // 3934 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock) 3935 { 3936 // 3937 // Fail 3938 // 3939 return STATUS_INVALID_PARAMETER; 3940 } 3941 3942 // 3943 // 0 is also illegal 3944 // 3945 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER; 3946 3947 // 3948 // Get a reference to the process 3949 // 3950 Status = ObReferenceObjectByHandle(ProcessHandle, 3951 PROCESS_VM_OPERATION, 3952 PsProcessType, 3953 PreviousMode, 3954 (PVOID*)(&Process), 3955 NULL); 3956 if (!NT_SUCCESS(Status)) return Status; 3957 3958 // 3959 // Check if this is is system-mapped 3960 // 3961 if (MapType & MAP_SYSTEM) 3962 { 3963 // 3964 // Check for required privilege 3965 // 3966 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 3967 { 3968 // 3969 // Fail: Don't have it 3970 // 3971 ObDereferenceObject(Process); 3972 return STATUS_PRIVILEGE_NOT_HELD; 3973 } 3974 } 3975 3976 // 3977 // Check if we should attach 3978 // 3979 if (CurrentProcess != Process) 3980 { 3981 // 3982 // Do it 3983 // 3984 KeStackAttachProcess(&Process->Pcb, &ApcState); 3985 Attached = TRUE; 3986 } 3987 3988 // 3989 // Call the internal function 3990 // 3991 Status = MiUnlockVirtualMemory(&CapturedBaseAddress, 3992 &CapturedBytesToUnlock, 3993 MapType); 3994 3995 // 3996 // Detach if needed 3997 // 3998 if (Attached) KeUnstackDetachProcess(&ApcState); 3999 4000 // 4001 // Release reference 4002 // 4003 ObDereferenceObject(Process); 4004 4005 // 4006 // Enter SEH to return data 4007 // 4008 _SEH2_TRY 4009 { 4010 // 4011 // Return data to user 4012 // 4013 *BaseAddress = CapturedBaseAddress; 4014 *NumberOfBytesToUnlock = CapturedBytesToUnlock; 4015 } 4016 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4017 { 4018 // 4019 // Get exception code 4020 // 4021 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4022 } 4023 _SEH2_END; 4024 4025 // 4026 // Return status 4027 // 4028 return STATUS_SUCCESS; 4029 } 4030 4031 NTSTATUS 4032 NTAPI 4033 NtFlushVirtualMemory(IN HANDLE ProcessHandle, 4034 IN OUT PVOID *BaseAddress, 4035 IN OUT PSIZE_T NumberOfBytesToFlush, 4036 OUT PIO_STATUS_BLOCK IoStatusBlock) 4037 { 4038 PEPROCESS Process; 4039 NTSTATUS Status; 4040 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4041 PVOID CapturedBaseAddress; 4042 SIZE_T CapturedBytesToFlush; 4043 IO_STATUS_BLOCK LocalStatusBlock; 4044 PAGED_CODE(); 4045 4046 // 4047 // Check if we came from user mode 4048 // 4049 if (PreviousMode != KernelMode) 4050 { 4051 // 4052 // Enter SEH for probing 4053 // 4054 _SEH2_TRY 4055 { 4056 // 4057 // Validate all outputs 4058 // 4059 ProbeForWritePointer(BaseAddress); 4060 ProbeForWriteSize_t(NumberOfBytesToFlush); 4061 ProbeForWriteIoStatusBlock(IoStatusBlock); 4062 4063 // 4064 // Capture them 4065 // 4066 CapturedBaseAddress = *BaseAddress; 4067 CapturedBytesToFlush = *NumberOfBytesToFlush; 4068 } 4069 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4070 { 4071 // 4072 // Get exception code 4073 // 4074 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4075 } 4076 _SEH2_END; 4077 } 4078 else 4079 { 4080 // 4081 // Capture directly 4082 // 4083 CapturedBaseAddress = *BaseAddress; 4084 CapturedBytesToFlush = *NumberOfBytesToFlush; 4085 } 4086 4087 // 4088 // Catch illegal base address 4089 // 4090 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 4091 4092 // 4093 // Catch illegal region size 4094 // 4095 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush) 4096 { 4097 // 4098 // Fail 4099 // 4100 return STATUS_INVALID_PARAMETER; 4101 } 4102 4103 // 4104 // Get a reference to the process 4105 // 4106 Status = ObReferenceObjectByHandle(ProcessHandle, 4107 PROCESS_VM_OPERATION, 4108 PsProcessType, 4109 PreviousMode, 4110 (PVOID*)(&Process), 4111 NULL); 4112 if (!NT_SUCCESS(Status)) return Status; 4113 4114 // 4115 // Do it 4116 // 4117 Status = MmFlushVirtualMemory(Process, 4118 &CapturedBaseAddress, 4119 &CapturedBytesToFlush, 4120 &LocalStatusBlock); 4121 4122 // 4123 // Release reference 4124 // 4125 ObDereferenceObject(Process); 4126 4127 // 4128 // Enter SEH to return data 4129 // 4130 _SEH2_TRY 4131 { 4132 // 4133 // Return data to user 4134 // 4135 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); 4136 *NumberOfBytesToFlush = 0; 4137 *IoStatusBlock = LocalStatusBlock; 4138 } 4139 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4140 { 4141 } 4142 _SEH2_END; 4143 4144 // 4145 // Return status 4146 // 4147 return Status; 4148 } 4149 4150 /* 4151 * @unimplemented 4152 */ 4153 NTSTATUS 4154 NTAPI 4155 NtGetWriteWatch(IN HANDLE ProcessHandle, 4156 IN ULONG Flags, 4157 IN PVOID BaseAddress, 4158 IN SIZE_T RegionSize, 4159 IN PVOID *UserAddressArray, 4160 OUT PULONG_PTR EntriesInUserAddressArray, 4161 OUT PULONG Granularity) 4162 { 4163 PEPROCESS Process; 4164 NTSTATUS Status; 4165 PVOID EndAddress; 4166 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4167 ULONG_PTR CapturedEntryCount; 4168 PAGED_CODE(); 4169 4170 // 4171 // Check if we came from user mode 4172 // 4173 if (PreviousMode != KernelMode) 4174 { 4175 // 4176 // Enter SEH for probing 4177 // 4178 _SEH2_TRY 4179 { 4180 // 4181 // Catch illegal base address 4182 // 4183 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2); 4184 4185 // 4186 // Catch illegal region size 4187 // 4188 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 4189 { 4190 // 4191 // Fail 4192 // 4193 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3); 4194 } 4195 4196 // 4197 // Validate all data 4198 // 4199 ProbeForWriteSize_t(EntriesInUserAddressArray); 4200 ProbeForWriteUlong(Granularity); 4201 4202 // 4203 // Capture them 4204 // 4205 CapturedEntryCount = *EntriesInUserAddressArray; 4206 4207 // 4208 // Must have a count 4209 // 4210 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5); 4211 4212 // 4213 // Can't be larger than the maximum 4214 // 4215 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR))) 4216 { 4217 // 4218 // Fail 4219 // 4220 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5); 4221 } 4222 4223 // 4224 // Probe the actual array 4225 // 4226 ProbeForWrite(UserAddressArray, 4227 CapturedEntryCount * sizeof(PVOID), 4228 sizeof(PVOID)); 4229 } 4230 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4231 { 4232 // 4233 // Get exception code 4234 // 4235 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4236 } 4237 _SEH2_END; 4238 } 4239 else 4240 { 4241 // 4242 // Capture directly 4243 // 4244 CapturedEntryCount = *EntriesInUserAddressArray; 4245 ASSERT(CapturedEntryCount != 0); 4246 } 4247 4248 // 4249 // Check if this is a local request 4250 // 4251 if (ProcessHandle == NtCurrentProcess()) 4252 { 4253 // 4254 // No need to reference the process 4255 // 4256 Process = PsGetCurrentProcess(); 4257 } 4258 else 4259 { 4260 // 4261 // Reference the target 4262 // 4263 Status = ObReferenceObjectByHandle(ProcessHandle, 4264 PROCESS_VM_OPERATION, 4265 PsProcessType, 4266 PreviousMode, 4267 (PVOID *)&Process, 4268 NULL); 4269 if (!NT_SUCCESS(Status)) return Status; 4270 } 4271 4272 // 4273 // Compute the last address and validate it 4274 // 4275 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 4276 if (BaseAddress > EndAddress) 4277 { 4278 // 4279 // Fail 4280 // 4281 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4282 return STATUS_INVALID_PARAMETER_4; 4283 } 4284 4285 // 4286 // Oops :( 4287 // 4288 UNIMPLEMENTED; 4289 4290 // 4291 // Dereference if needed 4292 // 4293 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4294 4295 // 4296 // Enter SEH to return data 4297 // 4298 _SEH2_TRY 4299 { 4300 // 4301 // Return data to user 4302 // 4303 *EntriesInUserAddressArray = 0; 4304 *Granularity = PAGE_SIZE; 4305 } 4306 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4307 { 4308 // 4309 // Get exception code 4310 // 4311 Status = _SEH2_GetExceptionCode(); 4312 } 4313 _SEH2_END; 4314 4315 // 4316 // Return success 4317 // 4318 return STATUS_SUCCESS; 4319 } 4320 4321 /* 4322 * @unimplemented 4323 */ 4324 NTSTATUS 4325 NTAPI 4326 NtResetWriteWatch(IN HANDLE ProcessHandle, 4327 IN PVOID BaseAddress, 4328 IN SIZE_T RegionSize) 4329 { 4330 PVOID EndAddress; 4331 PEPROCESS Process; 4332 NTSTATUS Status; 4333 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4334 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); 4335 4336 // 4337 // Catch illegal base address 4338 // 4339 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 4340 4341 // 4342 // Catch illegal region size 4343 // 4344 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 4345 { 4346 // 4347 // Fail 4348 // 4349 return STATUS_INVALID_PARAMETER_3; 4350 } 4351 4352 // 4353 // Check if this is a local request 4354 // 4355 if (ProcessHandle == NtCurrentProcess()) 4356 { 4357 // 4358 // No need to reference the process 4359 // 4360 Process = PsGetCurrentProcess(); 4361 } 4362 else 4363 { 4364 // 4365 // Reference the target 4366 // 4367 Status = ObReferenceObjectByHandle(ProcessHandle, 4368 PROCESS_VM_OPERATION, 4369 PsProcessType, 4370 PreviousMode, 4371 (PVOID *)&Process, 4372 NULL); 4373 if (!NT_SUCCESS(Status)) return Status; 4374 } 4375 4376 // 4377 // Compute the last address and validate it 4378 // 4379 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 4380 if (BaseAddress > EndAddress) 4381 { 4382 // 4383 // Fail 4384 // 4385 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4386 return STATUS_INVALID_PARAMETER_3; 4387 } 4388 4389 // 4390 // Oops :( 4391 // 4392 UNIMPLEMENTED; 4393 4394 // 4395 // Dereference if needed 4396 // 4397 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4398 4399 // 4400 // Return success 4401 // 4402 return STATUS_SUCCESS; 4403 } 4404 4405 NTSTATUS 4406 NTAPI 4407 NtQueryVirtualMemory(IN HANDLE ProcessHandle, 4408 IN PVOID BaseAddress, 4409 IN MEMORY_INFORMATION_CLASS MemoryInformationClass, 4410 OUT PVOID MemoryInformation, 4411 IN SIZE_T MemoryInformationLength, 4412 OUT PSIZE_T ReturnLength) 4413 { 4414 NTSTATUS Status = STATUS_SUCCESS; 4415 KPROCESSOR_MODE PreviousMode; 4416 4417 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress); 4418 4419 /* Bail out if the address is invalid */ 4420 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 4421 4422 /* Probe return buffer */ 4423 PreviousMode = ExGetPreviousMode(); 4424 if (PreviousMode != KernelMode) 4425 { 4426 _SEH2_TRY 4427 { 4428 ProbeForWrite(MemoryInformation, 4429 MemoryInformationLength, 4430 sizeof(ULONG_PTR)); 4431 4432 if (ReturnLength) ProbeForWriteSize_t(ReturnLength); 4433 } 4434 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4435 { 4436 Status = _SEH2_GetExceptionCode(); 4437 } 4438 _SEH2_END; 4439 4440 if (!NT_SUCCESS(Status)) 4441 { 4442 return Status; 4443 } 4444 } 4445 4446 switch(MemoryInformationClass) 4447 { 4448 case MemoryBasicInformation: 4449 /* Validate the size information of the class */ 4450 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) 4451 { 4452 /* The size is invalid */ 4453 return STATUS_INFO_LENGTH_MISMATCH; 4454 } 4455 Status = MiQueryMemoryBasicInformation(ProcessHandle, 4456 BaseAddress, 4457 MemoryInformation, 4458 MemoryInformationLength, 4459 ReturnLength); 4460 break; 4461 4462 case MemorySectionName: 4463 /* Validate the size information of the class */ 4464 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME)) 4465 { 4466 /* The size is invalid */ 4467 return STATUS_INFO_LENGTH_MISMATCH; 4468 } 4469 Status = MiQueryMemorySectionName(ProcessHandle, 4470 BaseAddress, 4471 MemoryInformation, 4472 MemoryInformationLength, 4473 ReturnLength); 4474 break; 4475 case MemoryWorkingSetList: 4476 case MemoryBasicVlmInformation: 4477 default: 4478 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass); 4479 break; 4480 } 4481 4482 return Status; 4483 } 4484 4485 /* 4486 * @implemented 4487 */ 4488 NTSTATUS 4489 NTAPI 4490 NtAllocateVirtualMemory(IN HANDLE ProcessHandle, 4491 IN OUT PVOID* UBaseAddress, 4492 IN ULONG_PTR ZeroBits, 4493 IN OUT PSIZE_T URegionSize, 4494 IN ULONG AllocationType, 4495 IN ULONG Protect) 4496 { 4497 PEPROCESS Process; 4498 PMEMORY_AREA MemoryArea; 4499 PMMVAD Vad = NULL, FoundVad; 4500 NTSTATUS Status; 4501 PMMSUPPORT AddressSpace; 4502 PVOID PBaseAddress; 4503 ULONG_PTR PRegionSize, StartingAddress, EndingAddress; 4504 ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS; 4505 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 4506 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 4507 PETHREAD CurrentThread = PsGetCurrentThread(); 4508 KAPC_STATE ApcState; 4509 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0; 4510 BOOLEAN Attached = FALSE, ChangeProtection = FALSE, QuotaCharged = FALSE; 4511 MMPTE TempPte; 4512 PMMPTE PointerPte, LastPte; 4513 PMMPDE PointerPde; 4514 TABLE_SEARCH_RESULT Result; 4515 PAGED_CODE(); 4516 4517 /* Check for valid Zero bits */ 4518 if (ZeroBits > MI_MAX_ZERO_BITS) 4519 { 4520 DPRINT1("Too many zero bits\n"); 4521 return STATUS_INVALID_PARAMETER_3; 4522 } 4523 4524 /* Check for valid Allocation Types */ 4525 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL | 4526 MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES))) 4527 { 4528 DPRINT1("Invalid Allocation Type\n"); 4529 return STATUS_INVALID_PARAMETER_5; 4530 } 4531 4532 /* Check for at least one of these Allocation Types to be set */ 4533 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET))) 4534 { 4535 DPRINT1("No memory allocation base type\n"); 4536 return STATUS_INVALID_PARAMETER_5; 4537 } 4538 4539 /* MEM_RESET is an exclusive flag, make sure that is valid too */ 4540 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET)) 4541 { 4542 DPRINT1("Invalid use of MEM_RESET\n"); 4543 return STATUS_INVALID_PARAMETER_5; 4544 } 4545 4546 /* Check if large pages are being used */ 4547 if (AllocationType & MEM_LARGE_PAGES) 4548 { 4549 /* Large page allocations MUST be committed */ 4550 if (!(AllocationType & MEM_COMMIT)) 4551 { 4552 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n"); 4553 return STATUS_INVALID_PARAMETER_5; 4554 } 4555 4556 /* These flags are not allowed with large page allocations */ 4557 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH)) 4558 { 4559 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n"); 4560 return STATUS_INVALID_PARAMETER_5; 4561 } 4562 } 4563 4564 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */ 4565 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE)) 4566 { 4567 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n"); 4568 return STATUS_INVALID_PARAMETER_5; 4569 } 4570 4571 /* Check for valid MEM_PHYSICAL usage */ 4572 if (AllocationType & MEM_PHYSICAL) 4573 { 4574 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */ 4575 if (!(AllocationType & MEM_RESERVE)) 4576 { 4577 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n"); 4578 return STATUS_INVALID_PARAMETER_5; 4579 } 4580 4581 /* Only these flags are allowed with MEM_PHYSIAL */ 4582 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL)) 4583 { 4584 DPRINT1("Using illegal flags with MEM_PHYSICAL\n"); 4585 return STATUS_INVALID_PARAMETER_5; 4586 } 4587 4588 /* Then make sure PAGE_READWRITE is used */ 4589 if (Protect != PAGE_READWRITE) 4590 { 4591 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n"); 4592 return STATUS_INVALID_PARAMETER_6; 4593 } 4594 } 4595 4596 /* Calculate the protection mask and make sure it's valid */ 4597 ProtectionMask = MiMakeProtectionMask(Protect); 4598 if (ProtectionMask == MM_INVALID_PROTECTION) 4599 { 4600 DPRINT1("Invalid protection mask\n"); 4601 return STATUS_INVALID_PAGE_PROTECTION; 4602 } 4603 4604 /* Enter SEH */ 4605 _SEH2_TRY 4606 { 4607 /* Check for user-mode parameters */ 4608 if (PreviousMode != KernelMode) 4609 { 4610 /* Make sure they are writable */ 4611 ProbeForWritePointer(UBaseAddress); 4612 ProbeForWriteSize_t(URegionSize); 4613 } 4614 4615 /* Capture their values */ 4616 PBaseAddress = *UBaseAddress; 4617 PRegionSize = *URegionSize; 4618 } 4619 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4620 { 4621 /* Return the exception code */ 4622 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4623 } 4624 _SEH2_END; 4625 4626 /* Make sure the allocation isn't past the VAD area */ 4627 if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS) 4628 { 4629 DPRINT1("Virtual allocation base above User Space\n"); 4630 return STATUS_INVALID_PARAMETER_2; 4631 } 4632 4633 /* Make sure the allocation wouldn't overflow past the VAD area */ 4634 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize) 4635 { 4636 DPRINT1("Region size would overflow into kernel-memory\n"); 4637 return STATUS_INVALID_PARAMETER_4; 4638 } 4639 4640 /* Make sure there's a size specified */ 4641 if (!PRegionSize) 4642 { 4643 DPRINT1("Region size is invalid (zero)\n"); 4644 return STATUS_INVALID_PARAMETER_4; 4645 } 4646 4647 // 4648 // If this is for the current process, just use PsGetCurrentProcess 4649 // 4650 if (ProcessHandle == NtCurrentProcess()) 4651 { 4652 Process = CurrentProcess; 4653 } 4654 else 4655 { 4656 // 4657 // Otherwise, reference the process with VM rights and attach to it if 4658 // this isn't the current process. We must attach because we'll be touching 4659 // PTEs and PDEs that belong to user-mode memory, and also touching the 4660 // Working Set which is stored in Hyperspace. 4661 // 4662 Status = ObReferenceObjectByHandle(ProcessHandle, 4663 PROCESS_VM_OPERATION, 4664 PsProcessType, 4665 PreviousMode, 4666 (PVOID*)&Process, 4667 NULL); 4668 if (!NT_SUCCESS(Status)) return Status; 4669 if (CurrentProcess != Process) 4670 { 4671 KeStackAttachProcess(&Process->Pcb, &ApcState); 4672 Attached = TRUE; 4673 } 4674 } 4675 4676 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n", 4677 Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect); 4678 4679 // 4680 // Check for large page allocations and make sure that the required privilege 4681 // is being held, before attempting to handle them. 4682 // 4683 if ((AllocationType & MEM_LARGE_PAGES) && 4684 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))) 4685 { 4686 /* Fail without it */ 4687 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n"); 4688 Status = STATUS_PRIVILEGE_NOT_HELD; 4689 goto FailPathNoLock; 4690 } 4691 4692 // 4693 // Fail on the things we don't yet support 4694 // 4695 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES) 4696 { 4697 DPRINT1("MEM_LARGE_PAGES not supported\n"); 4698 Status = STATUS_INVALID_PARAMETER; 4699 goto FailPathNoLock; 4700 } 4701 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL) 4702 { 4703 DPRINT1("MEM_PHYSICAL not supported\n"); 4704 Status = STATUS_INVALID_PARAMETER; 4705 goto FailPathNoLock; 4706 } 4707 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH) 4708 { 4709 DPRINT1("MEM_WRITE_WATCH not supported\n"); 4710 Status = STATUS_INVALID_PARAMETER; 4711 goto FailPathNoLock; 4712 } 4713 4714 // 4715 // Check if the caller is reserving memory, or committing memory and letting 4716 // us pick the base address 4717 // 4718 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE)) 4719 { 4720 // 4721 // Do not allow COPY_ON_WRITE through this API 4722 // 4723 if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) 4724 { 4725 DPRINT1("Copy on write not allowed through this path\n"); 4726 Status = STATUS_INVALID_PAGE_PROTECTION; 4727 goto FailPathNoLock; 4728 } 4729 4730 // 4731 // Does the caller have an address in mind, or is this a blind commit? 4732 // 4733 if (!PBaseAddress) 4734 { 4735 // 4736 // This is a blind commit, all we need is the region size 4737 // 4738 PRegionSize = ROUND_TO_PAGES(PRegionSize); 4739 EndingAddress = 0; 4740 StartingAddress = 0; 4741 4742 // 4743 // Check if ZeroBits were specified 4744 // 4745 if (ZeroBits != 0) 4746 { 4747 // 4748 // Calculate the highest address and check if it's valid 4749 // 4750 HighestAddress = MAXULONG_PTR >> ZeroBits; 4751 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) 4752 { 4753 Status = STATUS_INVALID_PARAMETER_3; 4754 goto FailPathNoLock; 4755 } 4756 } 4757 } 4758 else 4759 { 4760 // 4761 // This is a reservation, so compute the starting address on the 4762 // expected 64KB granularity, and see where the ending address will 4763 // fall based on the aligned address and the passed in region size 4764 // 4765 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 4766 PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K); 4767 StartingAddress = (ULONG_PTR)PBaseAddress; 4768 } 4769 4770 // Charge quotas for the VAD 4771 Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 4772 if (!NT_SUCCESS(Status)) 4773 { 4774 DPRINT1("Quota exceeded.\n"); 4775 goto FailPathNoLock; 4776 } 4777 4778 QuotaCharged = TRUE; 4779 4780 // 4781 // Allocate and initialize the VAD 4782 // 4783 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); 4784 if (Vad == NULL) 4785 { 4786 DPRINT1("Failed to allocate a VAD!\n"); 4787 Status = STATUS_INSUFFICIENT_RESOURCES; 4788 goto FailPathNoLock; 4789 } 4790 4791 RtlZeroMemory(Vad, sizeof(MMVAD_LONG)); 4792 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1; 4793 Vad->u.VadFlags.Protection = ProtectionMask; 4794 Vad->u.VadFlags.PrivateMemory = 1; 4795 Vad->ControlArea = NULL; // For Memory-Area hack 4796 4797 // 4798 // Insert the VAD 4799 // 4800 Status = MiInsertVadEx(Vad, 4801 &StartingAddress, 4802 PRegionSize, 4803 HighestAddress, 4804 MM_VIRTMEM_GRANULARITY, 4805 AllocationType); 4806 if (!NT_SUCCESS(Status)) 4807 { 4808 DPRINT1("Failed to insert the VAD!\n"); 4809 ExFreePoolWithTag(Vad, 'SdaV'); 4810 goto FailPathNoLock; 4811 } 4812 4813 // 4814 // Detach and dereference the target process if 4815 // it was different from the current process 4816 // 4817 if (Attached) KeUnstackDetachProcess(&ApcState); 4818 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4819 4820 // 4821 // Use SEH to write back the base address and the region size. In the case 4822 // of an exception, we do not return back the exception code, as the memory 4823 // *has* been allocated. The caller would now have to call VirtualQuery 4824 // or do some other similar trick to actually find out where its memory 4825 // allocation ended up 4826 // 4827 _SEH2_TRY 4828 { 4829 *URegionSize = PRegionSize; 4830 *UBaseAddress = (PVOID)StartingAddress; 4831 } 4832 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4833 { 4834 // 4835 // Ignore exception! 4836 // 4837 } 4838 _SEH2_END; 4839 DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress); 4840 return STATUS_SUCCESS; 4841 } 4842 4843 // 4844 // This is a MEM_COMMIT on top of an existing address which must have been 4845 // MEM_RESERVED already. Compute the start and ending base addresses based 4846 // on the user input, and then compute the actual region size once all the 4847 // alignments have been done. 4848 // 4849 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1)); 4850 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 4851 PRegionSize = EndingAddress - StartingAddress + 1; 4852 4853 // 4854 // Lock the address space and make sure the process isn't already dead 4855 // 4856 AddressSpace = MmGetCurrentAddressSpace(); 4857 MmLockAddressSpace(AddressSpace); 4858 if (Process->VmDeleted) 4859 { 4860 DPRINT1("Process is dying\n"); 4861 Status = STATUS_PROCESS_IS_TERMINATING; 4862 goto FailPath; 4863 } 4864 4865 // 4866 // Get the VAD for this address range, and make sure it exists 4867 // 4868 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 4869 EndingAddress >> PAGE_SHIFT, 4870 &Process->VadRoot, 4871 (PMMADDRESS_NODE*)&FoundVad); 4872 if (Result != TableFoundNode) 4873 { 4874 DPRINT1("Could not find a VAD for this allocation\n"); 4875 Status = STATUS_CONFLICTING_ADDRESSES; 4876 goto FailPath; 4877 } 4878 4879 if ((AllocationType & MEM_RESET) == MEM_RESET) 4880 { 4881 /// @todo HACK: pretend success 4882 DPRINT("MEM_RESET not supported\n"); 4883 Status = STATUS_SUCCESS; 4884 goto FailPath; 4885 } 4886 4887 // 4888 // These kinds of VADs are illegal for this Windows function when trying to 4889 // commit an existing range 4890 // 4891 if ((FoundVad->u.VadFlags.VadType == VadAwe) || 4892 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 4893 (FoundVad->u.VadFlags.VadType == VadLargePages)) 4894 { 4895 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n"); 4896 Status = STATUS_CONFLICTING_ADDRESSES; 4897 goto FailPath; 4898 } 4899 4900 // 4901 // Make sure that this address range actually fits within the VAD for it 4902 // 4903 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) || 4904 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn)) 4905 { 4906 DPRINT1("Address range does not fit into the VAD\n"); 4907 Status = STATUS_CONFLICTING_ADDRESSES; 4908 goto FailPath; 4909 } 4910 4911 // 4912 // Make sure this is an ARM3 section 4913 // 4914 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress)); 4915 ASSERT(MemoryArea != NULL); 4916 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3) 4917 { 4918 DPRINT1("Illegal commit of non-ARM3 section!\n"); 4919 Status = STATUS_ALREADY_COMMITTED; 4920 goto FailPath; 4921 } 4922 4923 // Is this a previously reserved section being committed? If so, enter the 4924 // special section path 4925 // 4926 if (FoundVad->u.VadFlags.PrivateMemory == FALSE) 4927 { 4928 // 4929 // You cannot commit large page sections through this API 4930 // 4931 if (FoundVad->u.VadFlags.VadType == VadLargePageSection) 4932 { 4933 DPRINT1("Large page sections cannot be VirtualAlloc'd\n"); 4934 Status = STATUS_INVALID_PAGE_PROTECTION; 4935 goto FailPath; 4936 } 4937 4938 // 4939 // You can only use caching flags on a rotate VAD 4940 // 4941 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) && 4942 (FoundVad->u.VadFlags.VadType != VadRotatePhysical)) 4943 { 4944 DPRINT1("Cannot use caching flags with anything but rotate VADs\n"); 4945 Status = STATUS_INVALID_PAGE_PROTECTION; 4946 goto FailPath; 4947 } 4948 4949 // 4950 // We should make sure that the section's permissions aren't being 4951 // messed with 4952 // 4953 if (FoundVad->u.VadFlags.NoChange) 4954 { 4955 // 4956 // Make sure it's okay to touch it 4957 // Note: The Windows 2003 kernel has a bug here, passing the 4958 // unaligned base address together with the aligned size, 4959 // potentially covering a region larger than the actual allocation. 4960 // Might be exposed through NtGdiCreateDIBSection w/ section handle 4961 // For now we keep this behavior. 4962 // TODO: analyze possible implications, create test case 4963 // 4964 Status = MiCheckSecuredVad(FoundVad, 4965 PBaseAddress, 4966 PRegionSize, 4967 ProtectionMask); 4968 if (!NT_SUCCESS(Status)) 4969 { 4970 DPRINT1("Secured VAD being messed around with\n"); 4971 goto FailPath; 4972 } 4973 } 4974 4975 // 4976 // ARM3 does not support file-backed sections, only shared memory 4977 // 4978 ASSERT(FoundVad->ControlArea->FilePointer == NULL); 4979 4980 // 4981 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write 4982 // 4983 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) && 4984 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD))) 4985 { 4986 DPRINT1("Invalid page protection for rotate VAD\n"); 4987 Status = STATUS_INVALID_PAGE_PROTECTION; 4988 goto FailPath; 4989 } 4990 4991 // 4992 // Compute PTE addresses and the quota charge, then grab the commit lock 4993 // 4994 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT); 4995 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT); 4996 QuotaCharge = (ULONG)(LastPte - PointerPte + 1); 4997 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); 4998 4999 // 5000 // Get the segment template PTE and start looping each page 5001 // 5002 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate; 5003 ASSERT(TempPte.u.Long != 0); 5004 while (PointerPte <= LastPte) 5005 { 5006 // 5007 // For each non-already-committed page, write the invalid template PTE 5008 // 5009 if (PointerPte->u.Long == 0) 5010 { 5011 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5012 } 5013 else 5014 { 5015 QuotaFree++; 5016 } 5017 PointerPte++; 5018 } 5019 5020 // 5021 // Now do the commit accounting and release the lock 5022 // 5023 ASSERT(QuotaCharge >= QuotaFree); 5024 QuotaCharge -= QuotaFree; 5025 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge; 5026 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 5027 5028 // 5029 // We are done with committing the section pages 5030 // 5031 Status = STATUS_SUCCESS; 5032 goto FailPath; 5033 } 5034 5035 // 5036 // This is a specific ReactOS check because we only use normal VADs 5037 // 5038 ASSERT(FoundVad->u.VadFlags.VadType == VadNone); 5039 5040 // 5041 // While this is an actual Windows check 5042 // 5043 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical); 5044 5045 // 5046 // Throw out attempts to use copy-on-write through this API path 5047 // 5048 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)) 5049 { 5050 DPRINT1("Write copy attempted when not allowed\n"); 5051 Status = STATUS_INVALID_PAGE_PROTECTION; 5052 goto FailPath; 5053 } 5054 5055 // 5056 // Initialize a demand-zero PTE 5057 // 5058 TempPte.u.Long = 0; 5059 TempPte.u.Soft.Protection = ProtectionMask; 5060 ASSERT(TempPte.u.Long != 0); 5061 5062 // 5063 // Get the PTE, PDE and the last PTE for this address range 5064 // 5065 PointerPde = MiAddressToPde(StartingAddress); 5066 PointerPte = MiAddressToPte(StartingAddress); 5067 LastPte = MiAddressToPte(EndingAddress); 5068 5069 // 5070 // Update the commit charge in the VAD as well as in the process, and check 5071 // if this commit charge was now higher than the last recorded peak, in which 5072 // case we also update the peak 5073 // 5074 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte); 5075 Process->CommitCharge += (1 + LastPte - PointerPte); 5076 if (Process->CommitCharge > Process->CommitChargePeak) 5077 { 5078 Process->CommitChargePeak = Process->CommitCharge; 5079 } 5080 5081 // 5082 // Lock the working set while we play with user pages and page tables 5083 // 5084 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5085 5086 // 5087 // Make the current page table valid, and then loop each page within it 5088 // 5089 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 5090 while (PointerPte <= LastPte) 5091 { 5092 // 5093 // Have we crossed into a new page table? 5094 // 5095 if (MiIsPteOnPdeBoundary(PointerPte)) 5096 { 5097 // 5098 // Get the PDE and now make it valid too 5099 // 5100 PointerPde = MiPteToPde(PointerPte); 5101 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 5102 } 5103 5104 // 5105 // Is this a zero PTE as expected? 5106 // 5107 if (PointerPte->u.Long == 0) 5108 { 5109 // 5110 // First increment the count of pages in the page table for this 5111 // process 5112 // 5113 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 5114 5115 // 5116 // And now write the invalid demand-zero PTE as requested 5117 // 5118 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5119 } 5120 else if (PointerPte->u.Long == MmDecommittedPte.u.Long) 5121 { 5122 // 5123 // If the PTE was already decommitted, there is nothing else to do 5124 // but to write the new demand-zero PTE 5125 // 5126 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5127 } 5128 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte))) 5129 { 5130 // 5131 // We don't handle these scenarios yet 5132 // 5133 if (PointerPte->u.Soft.Valid == 0) 5134 { 5135 ASSERT(PointerPte->u.Soft.Prototype == 0); 5136 ASSERT((PointerPte->u.Soft.PageFileHigh == 0) || (PointerPte->u.Soft.Transition == 1)); 5137 } 5138 5139 // 5140 // There's a change in protection, remember this for later, but do 5141 // not yet handle it. 5142 // 5143 ChangeProtection = TRUE; 5144 } 5145 5146 // 5147 // Move to the next PTE 5148 // 5149 PointerPte++; 5150 } 5151 5152 // 5153 // Release the working set lock, unlock the address space, and detach from 5154 // the target process if it was not the current process. Also dereference the 5155 // target process if this wasn't the case. 5156 // 5157 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 5158 Status = STATUS_SUCCESS; 5159 FailPath: 5160 MmUnlockAddressSpace(AddressSpace); 5161 5162 if (!NT_SUCCESS(Status)) 5163 { 5164 if (Vad != NULL) 5165 { 5166 ExFreePoolWithTag(Vad, 'SdaV'); 5167 } 5168 } 5169 5170 // 5171 // Check if we need to update the protection 5172 // 5173 if (ChangeProtection) 5174 { 5175 PVOID ProtectBaseAddress = (PVOID)StartingAddress; 5176 SIZE_T ProtectSize = PRegionSize; 5177 ULONG OldProtection; 5178 5179 // 5180 // Change the protection of the region 5181 // 5182 MiProtectVirtualMemory(Process, 5183 &ProtectBaseAddress, 5184 &ProtectSize, 5185 Protect, 5186 &OldProtection); 5187 } 5188 5189 FailPathNoLock: 5190 if (Attached) KeUnstackDetachProcess(&ApcState); 5191 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5192 5193 // 5194 // Only write back results on success 5195 // 5196 if (NT_SUCCESS(Status)) 5197 { 5198 // 5199 // Use SEH to write back the base address and the region size. In the case 5200 // of an exception, we strangely do return back the exception code, even 5201 // though the memory *has* been allocated. This mimics Windows behavior and 5202 // there is not much we can do about it. 5203 // 5204 _SEH2_TRY 5205 { 5206 *URegionSize = PRegionSize; 5207 *UBaseAddress = (PVOID)StartingAddress; 5208 } 5209 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5210 { 5211 Status = _SEH2_GetExceptionCode(); 5212 } 5213 _SEH2_END; 5214 } 5215 else if (QuotaCharged) 5216 { 5217 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5218 } 5219 5220 return Status; 5221 } 5222 5223 /* 5224 * @implemented 5225 */ 5226 NTSTATUS 5227 NTAPI 5228 NtFreeVirtualMemory(IN HANDLE ProcessHandle, 5229 IN PVOID* UBaseAddress, 5230 IN PSIZE_T URegionSize, 5231 IN ULONG FreeType) 5232 { 5233 PMEMORY_AREA MemoryArea; 5234 SIZE_T PRegionSize; 5235 PVOID PBaseAddress; 5236 LONG_PTR AlreadyDecommitted, CommitReduction = 0; 5237 LONG_PTR FirstCommit; 5238 ULONG_PTR StartingAddress, EndingAddress; 5239 PMMVAD Vad; 5240 PMMVAD NewVad; 5241 NTSTATUS Status; 5242 PEPROCESS Process; 5243 PMMSUPPORT AddressSpace; 5244 PETHREAD CurrentThread = PsGetCurrentThread(); 5245 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 5246 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 5247 KAPC_STATE ApcState; 5248 BOOLEAN Attached = FALSE; 5249 PAGED_CODE(); 5250 5251 // 5252 // Only two flags are supported, exclusively. 5253 // 5254 if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT) 5255 { 5256 DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType); 5257 return STATUS_INVALID_PARAMETER_4; 5258 } 5259 5260 // 5261 // Enter SEH for probe and capture. On failure, return back to the caller 5262 // with an exception violation. 5263 // 5264 _SEH2_TRY 5265 { 5266 // 5267 // Check for user-mode parameters and make sure that they are writeable 5268 // 5269 if (PreviousMode != KernelMode) 5270 { 5271 ProbeForWritePointer(UBaseAddress); 5272 ProbeForWriteUlong(URegionSize); 5273 } 5274 5275 // 5276 // Capture the current values 5277 // 5278 PBaseAddress = *UBaseAddress; 5279 PRegionSize = *URegionSize; 5280 } 5281 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5282 { 5283 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 5284 } 5285 _SEH2_END; 5286 5287 // 5288 // Make sure the allocation isn't past the user area 5289 // 5290 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS) 5291 { 5292 DPRINT1("Virtual free base above User Space\n"); 5293 return STATUS_INVALID_PARAMETER_2; 5294 } 5295 5296 // 5297 // Make sure the allocation wouldn't overflow past the user area 5298 // 5299 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize) 5300 { 5301 DPRINT1("Region size would overflow into kernel-memory\n"); 5302 return STATUS_INVALID_PARAMETER_3; 5303 } 5304 5305 // 5306 // If this is for the current process, just use PsGetCurrentProcess 5307 // 5308 if (ProcessHandle == NtCurrentProcess()) 5309 { 5310 Process = CurrentProcess; 5311 } 5312 else 5313 { 5314 // 5315 // Otherwise, reference the process with VM rights and attach to it if 5316 // this isn't the current process. We must attach because we'll be touching 5317 // PTEs and PDEs that belong to user-mode memory, and also touching the 5318 // Working Set which is stored in Hyperspace. 5319 // 5320 Status = ObReferenceObjectByHandle(ProcessHandle, 5321 PROCESS_VM_OPERATION, 5322 PsProcessType, 5323 PreviousMode, 5324 (PVOID*)&Process, 5325 NULL); 5326 if (!NT_SUCCESS(Status)) return Status; 5327 if (CurrentProcess != Process) 5328 { 5329 KeStackAttachProcess(&Process->Pcb, &ApcState); 5330 Attached = TRUE; 5331 } 5332 } 5333 5334 DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n", 5335 Process, PBaseAddress, PRegionSize, FreeType); 5336 5337 // 5338 // Lock the address space 5339 // 5340 AddressSpace = MmGetCurrentAddressSpace(); 5341 MmLockAddressSpace(AddressSpace); 5342 5343 // 5344 // If the address space is being deleted, fail the de-allocation since it's 5345 // too late to do anything about it 5346 // 5347 if (Process->VmDeleted) 5348 { 5349 DPRINT1("Process is dead\n"); 5350 Status = STATUS_PROCESS_IS_TERMINATING; 5351 goto FailPath; 5352 } 5353 5354 // 5355 // Compute start and end addresses, and locate the VAD 5356 // 5357 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 5358 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 5359 Vad = MiLocateAddress((PVOID)StartingAddress); 5360 if (!Vad) 5361 { 5362 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress); 5363 Status = STATUS_MEMORY_NOT_ALLOCATED; 5364 goto FailPath; 5365 } 5366 5367 // 5368 // If the range exceeds the VAD's ending VPN, fail this request 5369 // 5370 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT)) 5371 { 5372 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress); 5373 Status = STATUS_UNABLE_TO_FREE_VM; 5374 goto FailPath; 5375 } 5376 5377 // 5378 // Only private memory (except rotate VADs) can be freed through here */ 5379 // 5380 if ((!(Vad->u.VadFlags.PrivateMemory) && 5381 (Vad->u.VadFlags.VadType != VadRotatePhysical)) || 5382 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory)) 5383 { 5384 DPRINT("Attempt to free section memory\n"); 5385 Status = STATUS_UNABLE_TO_DELETE_SECTION; 5386 goto FailPath; 5387 } 5388 5389 // 5390 // ARM3 does not yet handle protected VM 5391 // 5392 ASSERT(Vad->u.VadFlags.NoChange == 0); 5393 5394 // 5395 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation 5396 // and that is is an ARM3 memory area, and not a section view, as we currently 5397 // don't support freeing those though this interface. 5398 // 5399 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress); 5400 ASSERT(MemoryArea); 5401 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3); 5402 5403 // 5404 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT 5405 // 5406 if (FreeType & MEM_RELEASE) 5407 { 5408 // 5409 // ARM3 only supports this VAD in this path 5410 // 5411 ASSERT(Vad->u.VadFlags.VadType == VadNone); 5412 5413 // 5414 // Is the caller trying to remove the whole VAD, or remove only a portion 5415 // of it? If no region size is specified, then the assumption is that the 5416 // whole VAD is to be destroyed 5417 // 5418 if (!PRegionSize) 5419 { 5420 // 5421 // The caller must specify the base address identically to the range 5422 // that is stored in the VAD. 5423 // 5424 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 5425 { 5426 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress); 5427 Status = STATUS_FREE_VM_NOT_AT_BASE; 5428 goto FailPath; 5429 } 5430 5431 // 5432 // Now compute the actual start/end addresses based on the VAD 5433 // 5434 StartingAddress = Vad->StartingVpn << PAGE_SHIFT; 5435 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 5436 5437 // 5438 // Finally lock the working set and remove the VAD from the VAD tree 5439 // 5440 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5441 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 5442 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 5443 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5444 } 5445 else 5446 { 5447 // 5448 // This means the caller wants to release a specific region within 5449 // the range. We have to find out which range this is -- the following 5450 // possibilities exist plus their union (CASE D): 5451 // 5452 // STARTING ADDRESS ENDING ADDRESS 5453 // [<========][========================================][=========>] 5454 // CASE A CASE B CASE C 5455 // 5456 // 5457 // First, check for case A or D 5458 // 5459 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn) 5460 { 5461 // 5462 // Check for case D 5463 // 5464 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 5465 { 5466 // 5467 // Case D (freeing the entire region) 5468 // 5469 // This is the easiest one to handle -- it is identical to 5470 // the code path above when the caller sets a zero region size 5471 // and the whole VAD is destroyed 5472 // 5473 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5474 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 5475 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 5476 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5477 } 5478 else 5479 { 5480 // 5481 // Case A (freeing a part at the beginning) 5482 // 5483 // This case is pretty easy too -- we compute a bunch of 5484 // pages to decommit, and then push the VAD's starting address 5485 // a bit further down, then decrement the commit charge 5486 // 5487 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5488 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5489 EndingAddress, 5490 Vad, 5491 Process); 5492 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5493 // For ReactOS: shrink the corresponding memory area 5494 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5495 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5496 Vad->StartingVpn = (EndingAddress + 1) >> PAGE_SHIFT; 5497 MemoryArea->VadNode.StartingVpn = Vad->StartingVpn; 5498 5499 // 5500 // After analyzing the VAD, set it to NULL so that we don't 5501 // free it in the exit path 5502 // 5503 Vad = NULL; 5504 } 5505 } 5506 else 5507 { 5508 // 5509 // This is case B or case C. First check for case C 5510 // 5511 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 5512 { 5513 // 5514 // Case C (freeing a part at the end) 5515 // 5516 // This is pretty easy and similar to case A. We compute the 5517 // amount of pages to decommit, update the VAD's commit charge 5518 // and then change the ending address of the VAD to be a bit 5519 // smaller. 5520 // 5521 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5522 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5523 EndingAddress, 5524 Vad, 5525 Process); 5526 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5527 // For ReactOS: shrink the corresponding memory area 5528 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5529 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5530 Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT; 5531 MemoryArea->VadNode.EndingVpn = Vad->EndingVpn; 5532 } 5533 else 5534 { 5535 // 5536 // Case B (freeing a part in the middle) 5537 // 5538 // This is the hardest one. Because we are removing a chunk 5539 // of memory from the very middle of the VAD, we must actually 5540 // split the VAD into two new VADs and compute the commit 5541 // charges for each of them, and reinsert new charges. 5542 // 5543 NewVad = ExAllocatePoolZero(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); 5544 if (NewVad == NULL) 5545 { 5546 DPRINT1("Failed to allocate a VAD!\n"); 5547 Status = STATUS_INSUFFICIENT_RESOURCES; 5548 goto FailPath; 5549 } 5550 5551 // Charge quota for the new VAD 5552 Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5553 5554 if (!NT_SUCCESS(Status)) 5555 { 5556 DPRINT1("Ran out of process quota whilst creating new VAD!\n"); 5557 ExFreePoolWithTag(NewVad, 'SdaV'); 5558 Status = STATUS_QUOTA_EXCEEDED; 5559 goto FailPath; 5560 } 5561 5562 // 5563 // This new VAD describes the second chunk, so we keep the end 5564 // address of the original and adjust the start to point past 5565 // the released region. 5566 // The commit charge will be calculated below. 5567 // 5568 NewVad->StartingVpn = (EndingAddress + 1) >> PAGE_SHIFT; 5569 NewVad->EndingVpn = Vad->EndingVpn; 5570 NewVad->u.LongFlags = Vad->u.LongFlags; 5571 NewVad->u.VadFlags.CommitCharge = 0; 5572 ASSERT(NewVad->EndingVpn >= NewVad->StartingVpn); 5573 5574 // 5575 // Get the commit charge for the released region 5576 // 5577 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5578 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5579 EndingAddress, 5580 Vad, 5581 Process); 5582 5583 // 5584 // Adjust the end of the original VAD (first chunk). 5585 // For ReactOS: shrink the corresponding memory area 5586 // 5587 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5588 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5589 Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT; 5590 MemoryArea->VadNode.EndingVpn = Vad->EndingVpn; 5591 5592 // 5593 // Now the addresses for both VADs are consistent, 5594 // so insert the new one. 5595 // ReactOS: This will take care of creating a second MEMORY_AREA. 5596 // 5597 MiInsertVad(NewVad, &Process->VadRoot); 5598 5599 // 5600 // Calculate the commit charge for the first split. 5601 // The second chunk's size is the original size, minus the 5602 // released region's size, minus this first chunk. 5603 // 5604 FirstCommit = MiCalculatePageCommitment(Vad->StartingVpn << PAGE_SHIFT, 5605 StartingAddress - 1, 5606 Vad, 5607 Process); 5608 NewVad->u.VadFlags.CommitCharge = Vad->u.VadFlags.CommitCharge - CommitReduction - FirstCommit; 5609 Vad->u.VadFlags.CommitCharge = FirstCommit; 5610 } 5611 5612 // 5613 // After analyzing the VAD, set it to NULL so that we don't 5614 // free it in the exit path 5615 // 5616 Vad = NULL; 5617 } 5618 } 5619 5620 // 5621 // Now we have a range of pages to dereference, so call the right API 5622 // to do that and then release the working set, since we're done messing 5623 // around with process pages. 5624 // 5625 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL); 5626 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 5627 Status = STATUS_SUCCESS; 5628 5629 FinalPath: 5630 // 5631 // Update the process counters 5632 // 5633 PRegionSize = EndingAddress - StartingAddress + 1; 5634 Process->CommitCharge -= CommitReduction; 5635 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize; 5636 5637 // 5638 // Unlock the address space and free the VAD in failure cases. Next, 5639 // detach from the target process so we can write the region size and the 5640 // base address to the correct source process, and dereference the target 5641 // process. 5642 // 5643 MmUnlockAddressSpace(AddressSpace); 5644 if (Vad) ExFreePool(Vad); 5645 if (Attached) KeUnstackDetachProcess(&ApcState); 5646 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5647 5648 // 5649 // Use SEH to safely return the region size and the base address of the 5650 // deallocation. If we get an access violation, don't return a failure code 5651 // as the deallocation *has* happened. The caller will just have to figure 5652 // out another way to find out where it is (such as VirtualQuery). 5653 // 5654 _SEH2_TRY 5655 { 5656 *URegionSize = PRegionSize; 5657 *UBaseAddress = (PVOID)StartingAddress; 5658 } 5659 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5660 { 5661 } 5662 _SEH2_END; 5663 return Status; 5664 } 5665 5666 // 5667 // This is the decommit path. You cannot decommit from the following VADs in 5668 // Windows, so fail the vall 5669 // 5670 if ((Vad->u.VadFlags.VadType == VadAwe) || 5671 (Vad->u.VadFlags.VadType == VadLargePages) || 5672 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 5673 { 5674 DPRINT1("Trying to decommit from invalid VAD\n"); 5675 Status = STATUS_MEMORY_NOT_ALLOCATED; 5676 goto FailPath; 5677 } 5678 5679 // 5680 // If the caller did not specify a region size, first make sure that this 5681 // region is actually committed. If it is, then compute the ending address 5682 // based on the VAD. 5683 // 5684 if (!PRegionSize) 5685 { 5686 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 5687 { 5688 DPRINT1("Decomitting non-committed memory\n"); 5689 Status = STATUS_FREE_VM_NOT_AT_BASE; 5690 goto FailPath; 5691 } 5692 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 5693 } 5694 5695 // 5696 // Decommit the PTEs for the range plus the actual backing pages for the 5697 // range, then reduce that amount from the commit charge in the VAD 5698 // 5699 AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress, 5700 MiAddressToPte(EndingAddress), 5701 Process, 5702 Vad); 5703 CommitReduction = MiAddressToPte(EndingAddress) - 5704 MiAddressToPte(StartingAddress) + 5705 1 - 5706 AlreadyDecommitted; 5707 5708 ASSERT(CommitReduction >= 0); 5709 ASSERT(Vad->u.VadFlags.CommitCharge >= CommitReduction); 5710 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5711 5712 // 5713 // We are done, go to the exit path without freeing the VAD as it remains 5714 // valid since we have not released the allocation. 5715 // 5716 Vad = NULL; 5717 Status = STATUS_SUCCESS; 5718 goto FinalPath; 5719 5720 // 5721 // In the failure path, we detach and dereference the target process, and 5722 // return whatever failure code was sent. 5723 // 5724 FailPath: 5725 MmUnlockAddressSpace(AddressSpace); 5726 if (Attached) KeUnstackDetachProcess(&ApcState); 5727 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5728 return Status; 5729 } 5730 5731 5732 PHYSICAL_ADDRESS 5733 NTAPI 5734 MmGetPhysicalAddress(PVOID Address) 5735 { 5736 PHYSICAL_ADDRESS PhysicalAddress; 5737 MMPDE TempPde; 5738 MMPTE TempPte; 5739 5740 /* Check if the PXE/PPE/PDE is valid */ 5741 if ( 5742 #if (_MI_PAGING_LEVELS == 4) 5743 (MiAddressToPxe(Address)->u.Hard.Valid) && 5744 #endif 5745 #if (_MI_PAGING_LEVELS >= 3) 5746 (MiAddressToPpe(Address)->u.Hard.Valid) && 5747 #endif 5748 (MiAddressToPde(Address)->u.Hard.Valid)) 5749 { 5750 /* Check for large pages */ 5751 TempPde = *MiAddressToPde(Address); 5752 if (TempPde.u.Hard.LargePage) 5753 { 5754 /* Physical address is base page + large page offset */ 5755 PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT; 5756 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1)); 5757 return PhysicalAddress; 5758 } 5759 5760 /* Check if the PTE is valid */ 5761 TempPte = *MiAddressToPte(Address); 5762 if (TempPte.u.Hard.Valid) 5763 { 5764 /* Physical address is base page + page offset */ 5765 PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT; 5766 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1)); 5767 return PhysicalAddress; 5768 } 5769 } 5770 5771 KeRosDumpStackFrames(NULL, 20); 5772 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address); 5773 PhysicalAddress.QuadPart = 0; 5774 return PhysicalAddress; 5775 } 5776 5777 5778 /* EOF */ 5779