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 /* Calculate base address for the VAD */ 2217 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress)); 2218 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1)); 2219 2220 /* Calculate the protection mask and make sure it's valid */ 2221 ProtectionMask = MiMakeProtectionMask(NewAccessProtection); 2222 if (ProtectionMask == MM_INVALID_PROTECTION) 2223 { 2224 DPRINT1("Invalid protection mask\n"); 2225 return STATUS_INVALID_PAGE_PROTECTION; 2226 } 2227 2228 /* Check for ROS specific memory area */ 2229 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress); 2230 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) 2231 { 2232 /* Evil hack */ 2233 return MiRosProtectVirtualMemory(Process, 2234 BaseAddress, 2235 NumberOfBytesToProtect, 2236 NewAccessProtection, 2237 OldAccessProtection); 2238 } 2239 2240 /* Lock the address space and make sure the process isn't already dead */ 2241 AddressSpace = MmGetCurrentAddressSpace(); 2242 MmLockAddressSpace(AddressSpace); 2243 if (Process->VmDeleted) 2244 { 2245 DPRINT1("Process is dying\n"); 2246 Status = STATUS_PROCESS_IS_TERMINATING; 2247 goto FailPath; 2248 } 2249 2250 /* Get the VAD for this address range, and make sure it exists */ 2251 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 2252 EndingAddress >> PAGE_SHIFT, 2253 &Process->VadRoot, 2254 (PMMADDRESS_NODE*)&Vad); 2255 if (Result != TableFoundNode) 2256 { 2257 DPRINT("Could not find a VAD for this allocation\n"); 2258 Status = STATUS_CONFLICTING_ADDRESSES; 2259 goto FailPath; 2260 } 2261 2262 /* Make sure the address is within this VAD's boundaries */ 2263 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) || 2264 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn)) 2265 { 2266 Status = STATUS_CONFLICTING_ADDRESSES; 2267 goto FailPath; 2268 } 2269 2270 /* These kinds of VADs are not supported atm */ 2271 if ((Vad->u.VadFlags.VadType == VadAwe) || 2272 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 2273 (Vad->u.VadFlags.VadType == VadLargePages)) 2274 { 2275 DPRINT1("Illegal VAD for attempting to set protection\n"); 2276 Status = STATUS_CONFLICTING_ADDRESSES; 2277 goto FailPath; 2278 } 2279 2280 /* Check for a VAD whose protection can't be changed */ 2281 if (Vad->u.VadFlags.NoChange == 1) 2282 { 2283 DPRINT1("Trying to change protection of a NoChange VAD\n"); 2284 Status = STATUS_INVALID_PAGE_PROTECTION; 2285 goto FailPath; 2286 } 2287 2288 /* Is this section, or private memory? */ 2289 if (Vad->u.VadFlags.PrivateMemory == 0) 2290 { 2291 /* Not yet supported */ 2292 if (Vad->u.VadFlags.VadType == VadLargePageSection) 2293 { 2294 DPRINT1("Illegal VAD for attempting to set protection\n"); 2295 Status = STATUS_CONFLICTING_ADDRESSES; 2296 goto FailPath; 2297 } 2298 2299 /* Rotate VADs are not yet supported */ 2300 if (Vad->u.VadFlags.VadType == VadRotatePhysical) 2301 { 2302 DPRINT1("Illegal VAD for attempting to set protection\n"); 2303 Status = STATUS_CONFLICTING_ADDRESSES; 2304 goto FailPath; 2305 } 2306 2307 /* Not valid on section files */ 2308 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) 2309 { 2310 /* Fail */ 2311 DPRINT1("Invalid protection flags for section\n"); 2312 Status = STATUS_INVALID_PARAMETER_4; 2313 goto FailPath; 2314 } 2315 2316 /* Check if data or page file mapping protection PTE is compatible */ 2317 if (!Vad->ControlArea->u.Flags.Image) 2318 { 2319 /* Not yet */ 2320 DPRINT1("Fixme: Not checking for valid protection\n"); 2321 } 2322 2323 /* This is a section, and this is not yet supported */ 2324 DPRINT1("Section protection not yet supported\n"); 2325 OldProtect = 0; 2326 } 2327 else 2328 { 2329 /* Private memory, check protection flags */ 2330 if ((NewAccessProtection & PAGE_WRITECOPY) || 2331 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY)) 2332 { 2333 DPRINT1("Invalid protection flags for private memory\n"); 2334 Status = STATUS_INVALID_PARAMETER_4; 2335 goto FailPath; 2336 } 2337 2338 /* Lock the working set */ 2339 MiLockProcessWorkingSetUnsafe(Process, Thread); 2340 2341 /* Check if all pages in this range are committed */ 2342 Committed = MiIsEntireRangeCommitted(StartingAddress, 2343 EndingAddress, 2344 Vad, 2345 Process); 2346 if (!Committed) 2347 { 2348 /* Fail */ 2349 DPRINT1("The entire range is not committed\n"); 2350 Status = STATUS_NOT_COMMITTED; 2351 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2352 goto FailPath; 2353 } 2354 2355 /* Compute starting and ending PTE and PDE addresses */ 2356 PointerPde = MiAddressToPde(StartingAddress); 2357 PointerPte = MiAddressToPte(StartingAddress); 2358 LastPte = MiAddressToPte(EndingAddress); 2359 2360 /* Make this PDE valid */ 2361 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2362 2363 /* Save protection of the first page */ 2364 if (PointerPte->u.Long != 0) 2365 { 2366 /* Capture the page protection and make the PDE valid */ 2367 OldProtect = MiGetPageProtection(PointerPte); 2368 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2369 } 2370 else 2371 { 2372 /* Grab the old protection from the VAD itself */ 2373 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; 2374 } 2375 2376 /* Loop all the PTEs now */ 2377 while (PointerPte <= LastPte) 2378 { 2379 /* Check if we've crossed a PDE boundary and make the new PDE valid too */ 2380 if (MiIsPteOnPdeBoundary(PointerPte)) 2381 { 2382 PointerPde = MiPteToPde(PointerPte); 2383 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2384 } 2385 2386 /* Capture the PTE and check if it was empty */ 2387 PteContents = *PointerPte; 2388 if (PteContents.u.Long == 0) 2389 { 2390 /* This used to be a zero PTE and it no longer is, so we must add a 2391 reference to the pagetable. */ 2392 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 2393 } 2394 2395 /* Check what kind of PTE we are dealing with */ 2396 if (PteContents.u.Hard.Valid == 1) 2397 { 2398 /* Get the PFN entry */ 2399 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); 2400 2401 /* We don't support this yet */ 2402 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 2403 2404 /* Check if the page should not be accessible at all */ 2405 if ((NewAccessProtection & PAGE_NOACCESS) || 2406 (NewAccessProtection & PAGE_GUARD)) 2407 { 2408 KIRQL OldIrql = MiAcquirePfnLock(); 2409 2410 /* Mark the PTE as transition and change its protection */ 2411 PteContents.u.Hard.Valid = 0; 2412 PteContents.u.Soft.Transition = 1; 2413 PteContents.u.Trans.Protection = ProtectionMask; 2414 /* Decrease PFN share count and write the PTE */ 2415 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); 2416 // FIXME: remove the page from the WS 2417 MI_WRITE_INVALID_PTE(PointerPte, PteContents); 2418 #ifdef CONFIG_SMP 2419 // FIXME: Should invalidate entry in every CPU TLB 2420 ASSERT(KeNumberProcessors == 1); 2421 #endif 2422 KeInvalidateTlbEntry(MiPteToAddress(PointerPte)); 2423 2424 /* We are done for this PTE */ 2425 MiReleasePfnLock(OldIrql); 2426 } 2427 else 2428 { 2429 /* Write the protection mask and write it with a TLB flush */ 2430 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; 2431 MiFlushTbAndCapture(Vad, 2432 PointerPte, 2433 ProtectionMask, 2434 Pfn1, 2435 TRUE); 2436 } 2437 } 2438 else 2439 { 2440 /* We don't support these cases yet */ 2441 ASSERT(PteContents.u.Soft.Prototype == 0); 2442 //ASSERT(PteContents.u.Soft.Transition == 0); 2443 2444 /* The PTE is already demand-zero, just update the protection mask */ 2445 PteContents.u.Soft.Protection = ProtectionMask; 2446 MI_WRITE_INVALID_PTE(PointerPte, PteContents); 2447 ASSERT(PointerPte->u.Long != 0); 2448 } 2449 2450 /* Move to the next PTE */ 2451 PointerPte++; 2452 } 2453 2454 /* Unlock the working set */ 2455 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2456 } 2457 2458 /* Unlock the address space */ 2459 MmUnlockAddressSpace(AddressSpace); 2460 2461 /* Return parameters and success */ 2462 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1; 2463 *BaseAddress = (PVOID)StartingAddress; 2464 *OldAccessProtection = OldProtect; 2465 return STATUS_SUCCESS; 2466 2467 FailPath: 2468 /* Unlock the address space and return the failure code */ 2469 MmUnlockAddressSpace(AddressSpace); 2470 return Status; 2471 } 2472 2473 VOID 2474 NTAPI 2475 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde, 2476 IN PEPROCESS TargetProcess, 2477 IN KIRQL OldIrql) 2478 { 2479 PMMPTE PointerPte; 2480 #if _MI_PAGING_LEVELS >= 3 2481 PMMPPE PointerPpe = MiPdeToPpe(PointerPde); 2482 #if _MI_PAGING_LEVELS == 4 2483 PMMPXE PointerPxe = MiPdeToPxe(PointerPde); 2484 #endif 2485 #endif 2486 2487 // 2488 // Sanity checks. The latter is because we only use this function with the 2489 // PFN lock not held, so it may go away in the future. 2490 // 2491 ASSERT(KeAreAllApcsDisabled() == TRUE); 2492 ASSERT(OldIrql == MM_NOIRQL); 2493 2494 // 2495 // If everything is already valid, there is nothing to do. 2496 // 2497 if ( 2498 #if _MI_PAGING_LEVELS == 4 2499 (PointerPxe->u.Hard.Valid) && 2500 #endif 2501 #if _MI_PAGING_LEVELS >= 3 2502 (PointerPpe->u.Hard.Valid) && 2503 #endif 2504 (PointerPde->u.Hard.Valid)) 2505 { 2506 return; 2507 } 2508 2509 // 2510 // At least something is invalid, so begin by getting the PTE for the PDE itself 2511 // and then lookup each additional level. We must do it in this precise order 2512 // because the pagfault.c code (as well as in Windows) depends that the next 2513 // level up (higher) must be valid when faulting a lower level 2514 // 2515 PointerPte = MiPteToAddress(PointerPde); 2516 do 2517 { 2518 // 2519 // Make sure APCs continued to be disabled 2520 // 2521 ASSERT(KeAreAllApcsDisabled() == TRUE); 2522 2523 #if _MI_PAGING_LEVELS == 4 2524 // 2525 // First, make the PXE valid if needed 2526 // 2527 if (!PointerPxe->u.Hard.Valid) 2528 { 2529 MiMakeSystemAddressValid(PointerPpe, TargetProcess); 2530 ASSERT(PointerPxe->u.Hard.Valid == 1); 2531 } 2532 #endif 2533 2534 #if _MI_PAGING_LEVELS >= 3 2535 // 2536 // Next, the PPE 2537 // 2538 if (!PointerPpe->u.Hard.Valid) 2539 { 2540 MiMakeSystemAddressValid(PointerPde, TargetProcess); 2541 ASSERT(PointerPpe->u.Hard.Valid == 1); 2542 } 2543 #endif 2544 2545 // 2546 // And finally, make the PDE itself valid. 2547 // 2548 MiMakeSystemAddressValid(PointerPte, TargetProcess); 2549 2550 /* Do not increment Page table refcount here for the PDE, this must be managed by caller */ 2551 2552 // 2553 // This should've worked the first time so the loop is really just for 2554 // show -- ASSERT that we're actually NOT going to be looping. 2555 // 2556 ASSERT(PointerPde->u.Hard.Valid == 1); 2557 } while ( 2558 #if _MI_PAGING_LEVELS == 4 2559 !PointerPxe->u.Hard.Valid || 2560 #endif 2561 #if _MI_PAGING_LEVELS >= 3 2562 !PointerPpe->u.Hard.Valid || 2563 #endif 2564 !PointerPde->u.Hard.Valid); 2565 } 2566 2567 VOID 2568 NTAPI 2569 MiProcessValidPteList(IN PMMPTE *ValidPteList, 2570 IN ULONG Count) 2571 { 2572 KIRQL OldIrql; 2573 ULONG i; 2574 MMPTE TempPte; 2575 PFN_NUMBER PageFrameIndex; 2576 PMMPFN Pfn1, Pfn2; 2577 2578 // 2579 // Acquire the PFN lock and loop all the PTEs in the list 2580 // 2581 OldIrql = MiAcquirePfnLock(); 2582 for (i = 0; i != Count; i++) 2583 { 2584 // 2585 // The PTE must currently be valid 2586 // 2587 TempPte = *ValidPteList[i]; 2588 ASSERT(TempPte.u.Hard.Valid == 1); 2589 2590 // 2591 // Get the PFN entry for the page itself, and then for its page table 2592 // 2593 PageFrameIndex = PFN_FROM_PTE(&TempPte); 2594 Pfn1 = MiGetPfnEntry(PageFrameIndex); 2595 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame); 2596 2597 // 2598 // Decrement the share count on the page table, and then on the page 2599 // itself 2600 // 2601 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame); 2602 MI_SET_PFN_DELETED(Pfn1); 2603 MiDecrementShareCount(Pfn1, PageFrameIndex); 2604 2605 // 2606 // Make the page decommitted 2607 // 2608 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte); 2609 } 2610 2611 // 2612 // All the PTEs have been dereferenced and made invalid, flush the TLB now 2613 // and then release the PFN lock 2614 // 2615 KeFlushCurrentTb(); 2616 MiReleasePfnLock(OldIrql); 2617 } 2618 2619 ULONG 2620 NTAPI 2621 MiDecommitPages(IN PVOID StartingAddress, 2622 IN PMMPTE EndingPte, 2623 IN PEPROCESS Process, 2624 IN PMMVAD Vad) 2625 { 2626 PMMPTE PointerPte, CommitPte = NULL; 2627 PMMPDE PointerPde; 2628 ULONG CommitReduction = 0; 2629 PMMPTE ValidPteList[256]; 2630 ULONG PteCount = 0; 2631 PMMPFN Pfn1; 2632 MMPTE PteContents; 2633 PETHREAD CurrentThread = PsGetCurrentThread(); 2634 2635 // 2636 // Get the PTE and PTE for the address, and lock the working set 2637 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the 2638 // commited range ends so that we can do the right accounting. 2639 // 2640 PointerPde = MiAddressToPde(StartingAddress); 2641 PointerPte = MiAddressToPte(StartingAddress); 2642 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT); 2643 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 2644 2645 // 2646 // Make the PDE valid, and now loop through each page's worth of data 2647 // 2648 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2649 while (PointerPte <= EndingPte) 2650 { 2651 // 2652 // Check if we've crossed a PDE boundary 2653 // 2654 if (MiIsPteOnPdeBoundary(PointerPte)) 2655 { 2656 // 2657 // Get the new PDE and flush the valid PTEs we had built up until 2658 // now. This helps reduce the amount of TLB flushing we have to do. 2659 // Note that Windows does a much better job using timestamps and 2660 // such, and does not flush the entire TLB all the time, but right 2661 // now we have bigger problems to worry about than TLB flushing. 2662 // 2663 PointerPde = MiAddressToPde(StartingAddress); 2664 if (PteCount) 2665 { 2666 MiProcessValidPteList(ValidPteList, PteCount); 2667 PteCount = 0; 2668 } 2669 2670 // 2671 // Make this PDE valid 2672 // 2673 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2674 } 2675 2676 // 2677 // Read this PTE. It might be active or still demand-zero. 2678 // 2679 PteContents = *PointerPte; 2680 if (PteContents.u.Long) 2681 { 2682 // 2683 // The PTE is active. It might be valid and in a working set, or 2684 // it might be a prototype PTE or paged out or even in transition. 2685 // 2686 if (PointerPte->u.Long == MmDecommittedPte.u.Long) 2687 { 2688 // 2689 // It's already decommited, so there's nothing for us to do here 2690 // 2691 CommitReduction++; 2692 } 2693 else 2694 { 2695 // 2696 // Remove it from the counters, and check if it was valid or not 2697 // 2698 //Process->NumberOfPrivatePages--; 2699 if (PteContents.u.Hard.Valid) 2700 { 2701 // 2702 // It's valid. At this point make sure that it is not a ROS 2703 // PFN. Also, we don't support ProtoPTEs in this code path. 2704 // 2705 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber); 2706 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE); 2707 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE); 2708 2709 // 2710 // Flush any pending PTEs that we had not yet flushed, if our 2711 // list has gotten too big, then add this PTE to the flush list. 2712 // 2713 if (PteCount == 256) 2714 { 2715 MiProcessValidPteList(ValidPteList, PteCount); 2716 PteCount = 0; 2717 } 2718 ValidPteList[PteCount++] = PointerPte; 2719 } 2720 else 2721 { 2722 // 2723 // We do not support any of these other scenarios at the moment 2724 // 2725 ASSERT(PteContents.u.Soft.Prototype == 0); 2726 ASSERT(PteContents.u.Soft.Transition == 0); 2727 ASSERT(PteContents.u.Soft.PageFileHigh == 0); 2728 2729 // 2730 // So the only other possibility is that it is still a demand 2731 // zero PTE, in which case we undo the accounting we did 2732 // earlier and simply make the page decommitted. 2733 // 2734 //Process->NumberOfPrivatePages++; 2735 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 2736 } 2737 } 2738 } 2739 else 2740 { 2741 // 2742 // This used to be a zero PTE and it no longer is, so we must add a 2743 // reference to the pagetable. 2744 // 2745 MiIncrementPageTableReferences(StartingAddress); 2746 2747 // 2748 // Next, we account for decommitted PTEs and make the PTE as such 2749 // 2750 if (PointerPte > CommitPte) CommitReduction++; 2751 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 2752 } 2753 2754 // 2755 // Move to the next PTE and the next address 2756 // 2757 PointerPte++; 2758 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE); 2759 } 2760 2761 // 2762 // Flush any dangling PTEs from the loop in the last page table, and then 2763 // release the working set and return the commit reduction accounting. 2764 // 2765 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount); 2766 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 2767 return CommitReduction; 2768 } 2769 2770 /* PUBLIC FUNCTIONS ***********************************************************/ 2771 2772 /* 2773 * @unimplemented 2774 */ 2775 PVOID 2776 NTAPI 2777 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress) 2778 { 2779 UNIMPLEMENTED; 2780 return 0; 2781 } 2782 2783 /* 2784 * @unimplemented 2785 */ 2786 PVOID 2787 NTAPI 2788 MmSecureVirtualMemory(IN PVOID Address, 2789 IN SIZE_T Length, 2790 IN ULONG Mode) 2791 { 2792 static ULONG Warn; if (!Warn++) UNIMPLEMENTED; 2793 return Address; 2794 } 2795 2796 /* 2797 * @unimplemented 2798 */ 2799 VOID 2800 NTAPI 2801 MmUnsecureVirtualMemory(IN PVOID SecureMem) 2802 { 2803 static ULONG Warn; if (!Warn++) UNIMPLEMENTED; 2804 } 2805 2806 /* SYSTEM CALLS ***************************************************************/ 2807 2808 NTSTATUS 2809 NTAPI 2810 NtReadVirtualMemory(IN HANDLE ProcessHandle, 2811 IN PVOID BaseAddress, 2812 OUT PVOID Buffer, 2813 IN SIZE_T NumberOfBytesToRead, 2814 OUT PSIZE_T NumberOfBytesRead OPTIONAL) 2815 { 2816 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2817 PEPROCESS Process; 2818 NTSTATUS Status = STATUS_SUCCESS; 2819 SIZE_T BytesRead = 0; 2820 PAGED_CODE(); 2821 2822 // 2823 // Check if we came from user mode 2824 // 2825 if (PreviousMode != KernelMode) 2826 { 2827 // 2828 // Validate the read addresses 2829 // 2830 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) || 2831 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) || 2832 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) || 2833 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress)) 2834 { 2835 // 2836 // Don't allow to write into kernel space 2837 // 2838 return STATUS_ACCESS_VIOLATION; 2839 } 2840 2841 // 2842 // Enter SEH for probe 2843 // 2844 _SEH2_TRY 2845 { 2846 // 2847 // Probe the output value 2848 // 2849 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead); 2850 } 2851 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2852 { 2853 // 2854 // Get exception code 2855 // 2856 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2857 } 2858 _SEH2_END; 2859 } 2860 2861 // 2862 // Don't do zero-byte transfers 2863 // 2864 if (NumberOfBytesToRead) 2865 { 2866 // 2867 // Reference the process 2868 // 2869 Status = ObReferenceObjectByHandle(ProcessHandle, 2870 PROCESS_VM_READ, 2871 PsProcessType, 2872 PreviousMode, 2873 (PVOID*)(&Process), 2874 NULL); 2875 if (NT_SUCCESS(Status)) 2876 { 2877 // 2878 // Do the copy 2879 // 2880 Status = MmCopyVirtualMemory(Process, 2881 BaseAddress, 2882 PsGetCurrentProcess(), 2883 Buffer, 2884 NumberOfBytesToRead, 2885 PreviousMode, 2886 &BytesRead); 2887 2888 // 2889 // Dereference the process 2890 // 2891 ObDereferenceObject(Process); 2892 } 2893 } 2894 2895 // 2896 // Check if the caller sent this parameter 2897 // 2898 if (NumberOfBytesRead) 2899 { 2900 // 2901 // Enter SEH to guard write 2902 // 2903 _SEH2_TRY 2904 { 2905 // 2906 // Return the number of bytes read 2907 // 2908 *NumberOfBytesRead = BytesRead; 2909 } 2910 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2911 { 2912 } 2913 _SEH2_END; 2914 } 2915 2916 // 2917 // Return status 2918 // 2919 return Status; 2920 } 2921 2922 NTSTATUS 2923 NTAPI 2924 NtWriteVirtualMemory(IN HANDLE ProcessHandle, 2925 IN PVOID BaseAddress, 2926 IN PVOID Buffer, 2927 IN SIZE_T NumberOfBytesToWrite, 2928 OUT PSIZE_T NumberOfBytesWritten OPTIONAL) 2929 { 2930 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2931 PEPROCESS Process; 2932 NTSTATUS Status = STATUS_SUCCESS; 2933 SIZE_T BytesWritten = 0; 2934 PAGED_CODE(); 2935 2936 // 2937 // Check if we came from user mode 2938 // 2939 if (PreviousMode != KernelMode) 2940 { 2941 // 2942 // Validate the read addresses 2943 // 2944 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) || 2945 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) || 2946 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) || 2947 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress)) 2948 { 2949 // 2950 // Don't allow to write into kernel space 2951 // 2952 return STATUS_ACCESS_VIOLATION; 2953 } 2954 2955 // 2956 // Enter SEH for probe 2957 // 2958 _SEH2_TRY 2959 { 2960 // 2961 // Probe the output value 2962 // 2963 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten); 2964 } 2965 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2966 { 2967 // 2968 // Get exception code 2969 // 2970 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2971 } 2972 _SEH2_END; 2973 } 2974 2975 // 2976 // Don't do zero-byte transfers 2977 // 2978 if (NumberOfBytesToWrite) 2979 { 2980 // 2981 // Reference the process 2982 // 2983 Status = ObReferenceObjectByHandle(ProcessHandle, 2984 PROCESS_VM_WRITE, 2985 PsProcessType, 2986 PreviousMode, 2987 (PVOID*)&Process, 2988 NULL); 2989 if (NT_SUCCESS(Status)) 2990 { 2991 // 2992 // Do the copy 2993 // 2994 Status = MmCopyVirtualMemory(PsGetCurrentProcess(), 2995 Buffer, 2996 Process, 2997 BaseAddress, 2998 NumberOfBytesToWrite, 2999 PreviousMode, 3000 &BytesWritten); 3001 3002 // 3003 // Dereference the process 3004 // 3005 ObDereferenceObject(Process); 3006 } 3007 } 3008 3009 // 3010 // Check if the caller sent this parameter 3011 // 3012 if (NumberOfBytesWritten) 3013 { 3014 // 3015 // Enter SEH to guard write 3016 // 3017 _SEH2_TRY 3018 { 3019 // 3020 // Return the number of bytes written 3021 // 3022 *NumberOfBytesWritten = BytesWritten; 3023 } 3024 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3025 { 3026 } 3027 _SEH2_END; 3028 } 3029 3030 // 3031 // Return status 3032 // 3033 return Status; 3034 } 3035 3036 NTSTATUS 3037 NTAPI 3038 NtFlushInstructionCache(_In_ HANDLE ProcessHandle, 3039 _In_opt_ PVOID BaseAddress, 3040 _In_ SIZE_T FlushSize) 3041 { 3042 KAPC_STATE ApcState; 3043 PKPROCESS Process; 3044 NTSTATUS Status; 3045 PAGED_CODE(); 3046 3047 /* Is a base address given? */ 3048 if (BaseAddress != NULL) 3049 { 3050 /* If the requested size is 0, there is nothing to do */ 3051 if (FlushSize == 0) 3052 { 3053 return STATUS_SUCCESS; 3054 } 3055 3056 /* Is this a user mode call? */ 3057 if (ExGetPreviousMode() != KernelMode) 3058 { 3059 /* Make sure the base address is in user space */ 3060 if (BaseAddress > MmHighestUserAddress) 3061 { 3062 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress); 3063 return STATUS_ACCESS_VIOLATION; 3064 } 3065 } 3066 } 3067 3068 /* Is another process requested? */ 3069 if (ProcessHandle != NtCurrentProcess()) 3070 { 3071 /* Reference the process */ 3072 Status = ObReferenceObjectByHandle(ProcessHandle, 3073 PROCESS_VM_WRITE, 3074 PsProcessType, 3075 ExGetPreviousMode(), 3076 (PVOID*)&Process, 3077 NULL); 3078 if (!NT_SUCCESS(Status)) 3079 { 3080 DPRINT1("Failed to reference the process %p\n", ProcessHandle); 3081 return Status; 3082 } 3083 3084 /* Attach to the process */ 3085 KeStackAttachProcess(Process, &ApcState); 3086 } 3087 3088 /* Forward to Ke */ 3089 KeSweepICache(BaseAddress, FlushSize); 3090 3091 /* Check if we attached */ 3092 if (ProcessHandle != NtCurrentProcess()) 3093 { 3094 /* Detach from the process and dereference it */ 3095 KeUnstackDetachProcess(&ApcState); 3096 ObDereferenceObject(Process); 3097 } 3098 3099 /* All done, return to caller */ 3100 return STATUS_SUCCESS; 3101 } 3102 3103 NTSTATUS 3104 NTAPI 3105 NtProtectVirtualMemory(IN HANDLE ProcessHandle, 3106 IN OUT PVOID *UnsafeBaseAddress, 3107 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect, 3108 IN ULONG NewAccessProtection, 3109 OUT PULONG UnsafeOldAccessProtection) 3110 { 3111 PEPROCESS Process; 3112 ULONG OldAccessProtection; 3113 ULONG Protection; 3114 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3115 PVOID BaseAddress = NULL; 3116 SIZE_T NumberOfBytesToProtect = 0; 3117 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3118 NTSTATUS Status; 3119 BOOLEAN Attached = FALSE; 3120 KAPC_STATE ApcState; 3121 PAGED_CODE(); 3122 3123 // 3124 // Check for valid protection flags 3125 // 3126 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE); 3127 if (Protection != PAGE_NOACCESS && 3128 Protection != PAGE_READONLY && 3129 Protection != PAGE_READWRITE && 3130 Protection != PAGE_WRITECOPY && 3131 Protection != PAGE_EXECUTE && 3132 Protection != PAGE_EXECUTE_READ && 3133 Protection != PAGE_EXECUTE_READWRITE && 3134 Protection != PAGE_EXECUTE_WRITECOPY) 3135 { 3136 // 3137 // Fail 3138 // 3139 return STATUS_INVALID_PAGE_PROTECTION; 3140 } 3141 3142 // 3143 // Check if we came from user mode 3144 // 3145 if (PreviousMode != KernelMode) 3146 { 3147 // 3148 // Enter SEH for probing 3149 // 3150 _SEH2_TRY 3151 { 3152 // 3153 // Validate all outputs 3154 // 3155 ProbeForWritePointer(UnsafeBaseAddress); 3156 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect); 3157 ProbeForWriteUlong(UnsafeOldAccessProtection); 3158 3159 // 3160 // Capture them 3161 // 3162 BaseAddress = *UnsafeBaseAddress; 3163 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 3164 } 3165 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3166 { 3167 // 3168 // Get exception code 3169 // 3170 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3171 } 3172 _SEH2_END; 3173 } 3174 else 3175 { 3176 // 3177 // Capture directly 3178 // 3179 BaseAddress = *UnsafeBaseAddress; 3180 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 3181 } 3182 3183 // 3184 // Catch illegal base address 3185 // 3186 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 3187 3188 // 3189 // Catch illegal region size 3190 // 3191 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect) 3192 { 3193 // 3194 // Fail 3195 // 3196 return STATUS_INVALID_PARAMETER_3; 3197 } 3198 3199 // 3200 // 0 is also illegal 3201 // 3202 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3; 3203 3204 // 3205 // Get a reference to the process 3206 // 3207 Status = ObReferenceObjectByHandle(ProcessHandle, 3208 PROCESS_VM_OPERATION, 3209 PsProcessType, 3210 PreviousMode, 3211 (PVOID*)(&Process), 3212 NULL); 3213 if (!NT_SUCCESS(Status)) return Status; 3214 3215 // 3216 // Check if we should attach 3217 // 3218 if (CurrentProcess != Process) 3219 { 3220 // 3221 // Do it 3222 // 3223 KeStackAttachProcess(&Process->Pcb, &ApcState); 3224 Attached = TRUE; 3225 } 3226 3227 // 3228 // Do the actual work 3229 // 3230 Status = MiProtectVirtualMemory(Process, 3231 &BaseAddress, 3232 &NumberOfBytesToProtect, 3233 NewAccessProtection, 3234 &OldAccessProtection); 3235 3236 // 3237 // Detach if needed 3238 // 3239 if (Attached) KeUnstackDetachProcess(&ApcState); 3240 3241 // 3242 // Release reference 3243 // 3244 ObDereferenceObject(Process); 3245 3246 // 3247 // Enter SEH to return data 3248 // 3249 _SEH2_TRY 3250 { 3251 // 3252 // Return data to user 3253 // 3254 *UnsafeOldAccessProtection = OldAccessProtection; 3255 *UnsafeBaseAddress = BaseAddress; 3256 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect; 3257 } 3258 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3259 { 3260 } 3261 _SEH2_END; 3262 3263 // 3264 // Return status 3265 // 3266 return Status; 3267 } 3268 3269 FORCEINLINE 3270 BOOLEAN 3271 MI_IS_LOCKED_VA( 3272 PMMPFN Pfn1, 3273 ULONG LockType) 3274 { 3275 // HACK until we have proper WSLIST support 3276 PMMWSLE Wsle = &Pfn1->Wsle; 3277 3278 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs)) 3279 return TRUE; 3280 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory)) 3281 return TRUE; 3282 3283 return FALSE; 3284 } 3285 3286 FORCEINLINE 3287 VOID 3288 MI_LOCK_VA( 3289 PMMPFN Pfn1, 3290 ULONG LockType) 3291 { 3292 // HACK until we have proper WSLIST support 3293 PMMWSLE Wsle = &Pfn1->Wsle; 3294 3295 if (!Wsle->u1.e1.LockedInWs && 3296 !Wsle->u1.e1.LockedInMemory) 3297 { 3298 MiReferenceProbedPageAndBumpLockCount(Pfn1); 3299 } 3300 3301 if (LockType & MAP_PROCESS) 3302 Wsle->u1.e1.LockedInWs = 1; 3303 if (LockType & MAP_SYSTEM) 3304 Wsle->u1.e1.LockedInMemory = 1; 3305 } 3306 3307 FORCEINLINE 3308 VOID 3309 MI_UNLOCK_VA( 3310 PMMPFN Pfn1, 3311 ULONG LockType) 3312 { 3313 // HACK until we have proper WSLIST support 3314 PMMWSLE Wsle = &Pfn1->Wsle; 3315 3316 if (LockType & MAP_PROCESS) 3317 Wsle->u1.e1.LockedInWs = 0; 3318 if (LockType & MAP_SYSTEM) 3319 Wsle->u1.e1.LockedInMemory = 0; 3320 3321 if (!Wsle->u1.e1.LockedInWs && 3322 !Wsle->u1.e1.LockedInMemory) 3323 { 3324 MiDereferencePfnAndDropLockCount(Pfn1); 3325 } 3326 } 3327 3328 static 3329 NTSTATUS 3330 MiCheckVadsForLockOperation( 3331 _Inout_ PVOID *BaseAddress, 3332 _Inout_ PSIZE_T RegionSize, 3333 _Inout_ PVOID *EndAddress) 3334 3335 { 3336 PMMVAD Vad; 3337 PVOID CurrentVa; 3338 3339 /* Get the base address and align the start address */ 3340 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize; 3341 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE); 3342 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE); 3343 3344 /* First loop and check all VADs */ 3345 CurrentVa = *BaseAddress; 3346 while (CurrentVa < *EndAddress) 3347 { 3348 /* Get VAD */ 3349 Vad = MiLocateAddress(CurrentVa); 3350 if (Vad == NULL) 3351 { 3352 /// FIXME: this might be a memory area for a section view... 3353 return STATUS_ACCESS_VIOLATION; 3354 } 3355 3356 /* Check VAD type */ 3357 if ((Vad->u.VadFlags.VadType != VadNone) && 3358 (Vad->u.VadFlags.VadType != VadImageMap) && 3359 (Vad->u.VadFlags.VadType != VadWriteWatch)) 3360 { 3361 *EndAddress = CurrentVa; 3362 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress; 3363 return STATUS_INCOMPATIBLE_FILE_MAP; 3364 } 3365 3366 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT); 3367 } 3368 3369 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress; 3370 return STATUS_SUCCESS; 3371 } 3372 3373 static 3374 NTSTATUS 3375 MiLockVirtualMemory( 3376 IN OUT PVOID *BaseAddress, 3377 IN OUT PSIZE_T RegionSize, 3378 IN ULONG MapType) 3379 { 3380 PEPROCESS CurrentProcess; 3381 PMMSUPPORT AddressSpace; 3382 PVOID CurrentVa, EndAddress; 3383 PMMPTE PointerPte, LastPte; 3384 PMMPDE PointerPde; 3385 #if (_MI_PAGING_LEVELS >= 3) 3386 PMMPDE PointerPpe; 3387 #endif 3388 #if (_MI_PAGING_LEVELS == 4) 3389 PMMPDE PointerPxe; 3390 #endif 3391 PMMPFN Pfn1; 3392 NTSTATUS Status, TempStatus; 3393 3394 /* Lock the address space */ 3395 AddressSpace = MmGetCurrentAddressSpace(); 3396 MmLockAddressSpace(AddressSpace); 3397 3398 /* Make sure we still have an address space */ 3399 CurrentProcess = PsGetCurrentProcess(); 3400 if (CurrentProcess->VmDeleted) 3401 { 3402 Status = STATUS_PROCESS_IS_TERMINATING; 3403 goto Cleanup; 3404 } 3405 3406 /* Check the VADs in the requested range */ 3407 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress); 3408 if (!NT_SUCCESS(Status)) 3409 { 3410 goto Cleanup; 3411 } 3412 3413 /* Enter SEH for probing */ 3414 _SEH2_TRY 3415 { 3416 /* Loop all pages and probe them */ 3417 CurrentVa = *BaseAddress; 3418 while (CurrentVa < EndAddress) 3419 { 3420 (void)(*(volatile CHAR*)CurrentVa); 3421 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE; 3422 } 3423 } 3424 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3425 { 3426 Status = _SEH2_GetExceptionCode(); 3427 goto Cleanup; 3428 } 3429 _SEH2_END; 3430 3431 /* All pages were accessible, since we hold the address space lock, nothing 3432 can be de-committed. Assume success for now. */ 3433 Status = STATUS_SUCCESS; 3434 3435 /* Get the PTE and PDE */ 3436 PointerPte = MiAddressToPte(*BaseAddress); 3437 PointerPde = MiAddressToPde(*BaseAddress); 3438 #if (_MI_PAGING_LEVELS >= 3) 3439 PointerPpe = MiAddressToPpe(*BaseAddress); 3440 #endif 3441 #if (_MI_PAGING_LEVELS == 4) 3442 PointerPxe = MiAddressToPxe(*BaseAddress); 3443 #endif 3444 3445 /* Get the last PTE */ 3446 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1)); 3447 3448 /* Lock the process working set */ 3449 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3450 3451 /* Loop the pages */ 3452 do 3453 { 3454 /* Check for a page that is not accessible */ 3455 while ( 3456 #if (_MI_PAGING_LEVELS == 4) 3457 (PointerPxe->u.Hard.Valid == 0) || 3458 #endif 3459 #if (_MI_PAGING_LEVELS >= 3) 3460 (PointerPpe->u.Hard.Valid == 0) || 3461 #endif 3462 (PointerPde->u.Hard.Valid == 0) || 3463 (PointerPte->u.Hard.Valid == 0)) 3464 { 3465 /* Release process working set */ 3466 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3467 3468 /* Access the page */ 3469 CurrentVa = MiPteToAddress(PointerPte); 3470 3471 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked 3472 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL); 3473 if (!NT_SUCCESS(TempStatus)) 3474 { 3475 // This should only happen, when remote backing storage is not accessible 3476 ASSERT(FALSE); 3477 Status = TempStatus; 3478 goto Cleanup; 3479 } 3480 3481 /* Lock the process working set */ 3482 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3483 } 3484 3485 /* Get the PFN */ 3486 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3487 ASSERT(Pfn1 != NULL); 3488 3489 /* Check the previous lock status */ 3490 if (MI_IS_LOCKED_VA(Pfn1, MapType)) 3491 { 3492 Status = STATUS_WAS_LOCKED; 3493 } 3494 3495 /* Lock it */ 3496 MI_LOCK_VA(Pfn1, MapType); 3497 3498 /* Go to the next PTE */ 3499 PointerPte++; 3500 3501 /* Check if we're on a PDE boundary */ 3502 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3503 #if (_MI_PAGING_LEVELS >= 3) 3504 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3505 #endif 3506 #if (_MI_PAGING_LEVELS == 4) 3507 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3508 #endif 3509 } while (PointerPte <= LastPte); 3510 3511 /* Release process working set */ 3512 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3513 3514 Cleanup: 3515 /* Unlock address space */ 3516 MmUnlockAddressSpace(AddressSpace); 3517 3518 return Status; 3519 } 3520 3521 NTSTATUS 3522 NTAPI 3523 NtLockVirtualMemory(IN HANDLE ProcessHandle, 3524 IN OUT PVOID *BaseAddress, 3525 IN OUT PSIZE_T NumberOfBytesToLock, 3526 IN ULONG MapType) 3527 { 3528 PEPROCESS Process; 3529 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3530 NTSTATUS Status; 3531 BOOLEAN Attached = FALSE; 3532 KAPC_STATE ApcState; 3533 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3534 PVOID CapturedBaseAddress; 3535 SIZE_T CapturedBytesToLock; 3536 PAGED_CODE(); 3537 3538 // 3539 // Validate flags 3540 // 3541 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 3542 { 3543 // 3544 // Invalid set of flags 3545 // 3546 return STATUS_INVALID_PARAMETER; 3547 } 3548 3549 // 3550 // At least one flag must be specified 3551 // 3552 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 3553 { 3554 // 3555 // No flag given 3556 // 3557 return STATUS_INVALID_PARAMETER; 3558 } 3559 3560 // 3561 // Enter SEH for probing 3562 // 3563 _SEH2_TRY 3564 { 3565 // 3566 // Validate output data 3567 // 3568 ProbeForWritePointer(BaseAddress); 3569 ProbeForWriteSize_t(NumberOfBytesToLock); 3570 3571 // 3572 // Capture it 3573 // 3574 CapturedBaseAddress = *BaseAddress; 3575 CapturedBytesToLock = *NumberOfBytesToLock; 3576 } 3577 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3578 { 3579 // 3580 // Get exception code 3581 // 3582 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3583 } 3584 _SEH2_END; 3585 3586 // 3587 // Catch illegal base address 3588 // 3589 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 3590 3591 // 3592 // Catch illegal region size 3593 // 3594 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock) 3595 { 3596 // 3597 // Fail 3598 // 3599 return STATUS_INVALID_PARAMETER; 3600 } 3601 3602 // 3603 // 0 is also illegal 3604 // 3605 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER; 3606 3607 // 3608 // Get a reference to the process 3609 // 3610 Status = ObReferenceObjectByHandle(ProcessHandle, 3611 PROCESS_VM_OPERATION, 3612 PsProcessType, 3613 PreviousMode, 3614 (PVOID*)(&Process), 3615 NULL); 3616 if (!NT_SUCCESS(Status)) return Status; 3617 3618 // 3619 // Check if this is is system-mapped 3620 // 3621 if (MapType & MAP_SYSTEM) 3622 { 3623 // 3624 // Check for required privilege 3625 // 3626 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 3627 { 3628 // 3629 // Fail: Don't have it 3630 // 3631 ObDereferenceObject(Process); 3632 return STATUS_PRIVILEGE_NOT_HELD; 3633 } 3634 } 3635 3636 // 3637 // Check if we should attach 3638 // 3639 if (CurrentProcess != Process) 3640 { 3641 // 3642 // Do it 3643 // 3644 KeStackAttachProcess(&Process->Pcb, &ApcState); 3645 Attached = TRUE; 3646 } 3647 3648 // 3649 // Call the internal function 3650 // 3651 Status = MiLockVirtualMemory(&CapturedBaseAddress, 3652 &CapturedBytesToLock, 3653 MapType); 3654 3655 // 3656 // Detach if needed 3657 // 3658 if (Attached) KeUnstackDetachProcess(&ApcState); 3659 3660 // 3661 // Release reference 3662 // 3663 ObDereferenceObject(Process); 3664 3665 // 3666 // Enter SEH to return data 3667 // 3668 _SEH2_TRY 3669 { 3670 // 3671 // Return data to user 3672 // 3673 *BaseAddress = CapturedBaseAddress; 3674 *NumberOfBytesToLock = CapturedBytesToLock; 3675 } 3676 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3677 { 3678 // 3679 // Get exception code 3680 // 3681 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3682 } 3683 _SEH2_END; 3684 3685 // 3686 // Return status 3687 // 3688 return Status; 3689 } 3690 3691 3692 static 3693 NTSTATUS 3694 MiUnlockVirtualMemory( 3695 IN OUT PVOID *BaseAddress, 3696 IN OUT PSIZE_T RegionSize, 3697 IN ULONG MapType) 3698 { 3699 PEPROCESS CurrentProcess; 3700 PMMSUPPORT AddressSpace; 3701 PVOID EndAddress; 3702 PMMPTE PointerPte, LastPte; 3703 PMMPDE PointerPde; 3704 #if (_MI_PAGING_LEVELS >= 3) 3705 PMMPDE PointerPpe; 3706 #endif 3707 #if (_MI_PAGING_LEVELS == 4) 3708 PMMPDE PointerPxe; 3709 #endif 3710 PMMPFN Pfn1; 3711 NTSTATUS Status; 3712 3713 /* Lock the address space */ 3714 AddressSpace = MmGetCurrentAddressSpace(); 3715 MmLockAddressSpace(AddressSpace); 3716 3717 /* Make sure we still have an address space */ 3718 CurrentProcess = PsGetCurrentProcess(); 3719 if (CurrentProcess->VmDeleted) 3720 { 3721 Status = STATUS_PROCESS_IS_TERMINATING; 3722 goto Cleanup; 3723 } 3724 3725 /* Check the VADs in the requested range */ 3726 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress); 3727 3728 /* Note: only bail out, if we hit an area without a VAD. If we hit an 3729 incompatible VAD we continue, like Windows does */ 3730 if (Status == STATUS_ACCESS_VIOLATION) 3731 { 3732 Status = STATUS_NOT_LOCKED; 3733 goto Cleanup; 3734 } 3735 3736 /* Get the PTE and PDE */ 3737 PointerPte = MiAddressToPte(*BaseAddress); 3738 PointerPde = MiAddressToPde(*BaseAddress); 3739 #if (_MI_PAGING_LEVELS >= 3) 3740 PointerPpe = MiAddressToPpe(*BaseAddress); 3741 #endif 3742 #if (_MI_PAGING_LEVELS == 4) 3743 PointerPxe = MiAddressToPxe(*BaseAddress); 3744 #endif 3745 3746 /* Get the last PTE */ 3747 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1)); 3748 3749 /* Lock the process working set */ 3750 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3751 3752 /* Loop the pages */ 3753 do 3754 { 3755 /* Check for a page that is not present */ 3756 if ( 3757 #if (_MI_PAGING_LEVELS == 4) 3758 (PointerPxe->u.Hard.Valid == 0) || 3759 #endif 3760 #if (_MI_PAGING_LEVELS >= 3) 3761 (PointerPpe->u.Hard.Valid == 0) || 3762 #endif 3763 (PointerPde->u.Hard.Valid == 0) || 3764 (PointerPte->u.Hard.Valid == 0)) 3765 { 3766 /* Remember it, but keep going */ 3767 Status = STATUS_NOT_LOCKED; 3768 } 3769 else 3770 { 3771 /* Get the PFN */ 3772 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3773 ASSERT(Pfn1 != NULL); 3774 3775 /* Check if all of the requested locks are present */ 3776 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) || 3777 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS))) 3778 { 3779 /* Remember it, but keep going */ 3780 Status = STATUS_NOT_LOCKED; 3781 3782 /* Check if no lock is present */ 3783 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM)) 3784 { 3785 DPRINT1("FIXME: Should remove the page from WS\n"); 3786 } 3787 } 3788 } 3789 3790 /* Go to the next PTE */ 3791 PointerPte++; 3792 3793 /* Check if we're on a PDE boundary */ 3794 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3795 #if (_MI_PAGING_LEVELS >= 3) 3796 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3797 #endif 3798 #if (_MI_PAGING_LEVELS == 4) 3799 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3800 #endif 3801 } while (PointerPte <= LastPte); 3802 3803 /* Check if we hit a page that was not locked */ 3804 if (Status == STATUS_NOT_LOCKED) 3805 { 3806 goto CleanupWithWsLock; 3807 } 3808 3809 /* All pages in the region were locked, so unlock them all */ 3810 3811 /* Get the PTE and PDE */ 3812 PointerPte = MiAddressToPte(*BaseAddress); 3813 PointerPde = MiAddressToPde(*BaseAddress); 3814 #if (_MI_PAGING_LEVELS >= 3) 3815 PointerPpe = MiAddressToPpe(*BaseAddress); 3816 #endif 3817 #if (_MI_PAGING_LEVELS == 4) 3818 PointerPxe = MiAddressToPxe(*BaseAddress); 3819 #endif 3820 3821 /* Loop the pages */ 3822 do 3823 { 3824 /* Unlock it */ 3825 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3826 MI_UNLOCK_VA(Pfn1, MapType); 3827 3828 /* Go to the next PTE */ 3829 PointerPte++; 3830 3831 /* Check if we're on a PDE boundary */ 3832 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3833 #if (_MI_PAGING_LEVELS >= 3) 3834 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3835 #endif 3836 #if (_MI_PAGING_LEVELS == 4) 3837 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3838 #endif 3839 } while (PointerPte <= LastPte); 3840 3841 /* Everything is done */ 3842 Status = STATUS_SUCCESS; 3843 3844 CleanupWithWsLock: 3845 3846 /* Release process working set */ 3847 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3848 3849 Cleanup: 3850 /* Unlock address space */ 3851 MmUnlockAddressSpace(AddressSpace); 3852 3853 return Status; 3854 } 3855 3856 3857 NTSTATUS 3858 NTAPI 3859 NtUnlockVirtualMemory(IN HANDLE ProcessHandle, 3860 IN OUT PVOID *BaseAddress, 3861 IN OUT PSIZE_T NumberOfBytesToUnlock, 3862 IN ULONG MapType) 3863 { 3864 PEPROCESS Process; 3865 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3866 NTSTATUS Status; 3867 BOOLEAN Attached = FALSE; 3868 KAPC_STATE ApcState; 3869 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3870 PVOID CapturedBaseAddress; 3871 SIZE_T CapturedBytesToUnlock; 3872 PAGED_CODE(); 3873 3874 // 3875 // Validate flags 3876 // 3877 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 3878 { 3879 // 3880 // Invalid set of flags 3881 // 3882 return STATUS_INVALID_PARAMETER; 3883 } 3884 3885 // 3886 // At least one flag must be specified 3887 // 3888 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 3889 { 3890 // 3891 // No flag given 3892 // 3893 return STATUS_INVALID_PARAMETER; 3894 } 3895 3896 // 3897 // Enter SEH for probing 3898 // 3899 _SEH2_TRY 3900 { 3901 // 3902 // Validate output data 3903 // 3904 ProbeForWritePointer(BaseAddress); 3905 ProbeForWriteSize_t(NumberOfBytesToUnlock); 3906 3907 // 3908 // Capture it 3909 // 3910 CapturedBaseAddress = *BaseAddress; 3911 CapturedBytesToUnlock = *NumberOfBytesToUnlock; 3912 } 3913 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3914 { 3915 // 3916 // Get exception code 3917 // 3918 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3919 } 3920 _SEH2_END; 3921 3922 // 3923 // Catch illegal base address 3924 // 3925 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 3926 3927 // 3928 // Catch illegal region size 3929 // 3930 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock) 3931 { 3932 // 3933 // Fail 3934 // 3935 return STATUS_INVALID_PARAMETER; 3936 } 3937 3938 // 3939 // 0 is also illegal 3940 // 3941 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER; 3942 3943 // 3944 // Get a reference to the process 3945 // 3946 Status = ObReferenceObjectByHandle(ProcessHandle, 3947 PROCESS_VM_OPERATION, 3948 PsProcessType, 3949 PreviousMode, 3950 (PVOID*)(&Process), 3951 NULL); 3952 if (!NT_SUCCESS(Status)) return Status; 3953 3954 // 3955 // Check if this is is system-mapped 3956 // 3957 if (MapType & MAP_SYSTEM) 3958 { 3959 // 3960 // Check for required privilege 3961 // 3962 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 3963 { 3964 // 3965 // Fail: Don't have it 3966 // 3967 ObDereferenceObject(Process); 3968 return STATUS_PRIVILEGE_NOT_HELD; 3969 } 3970 } 3971 3972 // 3973 // Check if we should attach 3974 // 3975 if (CurrentProcess != Process) 3976 { 3977 // 3978 // Do it 3979 // 3980 KeStackAttachProcess(&Process->Pcb, &ApcState); 3981 Attached = TRUE; 3982 } 3983 3984 // 3985 // Call the internal function 3986 // 3987 Status = MiUnlockVirtualMemory(&CapturedBaseAddress, 3988 &CapturedBytesToUnlock, 3989 MapType); 3990 3991 // 3992 // Detach if needed 3993 // 3994 if (Attached) KeUnstackDetachProcess(&ApcState); 3995 3996 // 3997 // Release reference 3998 // 3999 ObDereferenceObject(Process); 4000 4001 // 4002 // Enter SEH to return data 4003 // 4004 _SEH2_TRY 4005 { 4006 // 4007 // Return data to user 4008 // 4009 *BaseAddress = CapturedBaseAddress; 4010 *NumberOfBytesToUnlock = CapturedBytesToUnlock; 4011 } 4012 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4013 { 4014 // 4015 // Get exception code 4016 // 4017 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4018 } 4019 _SEH2_END; 4020 4021 // 4022 // Return status 4023 // 4024 return STATUS_SUCCESS; 4025 } 4026 4027 NTSTATUS 4028 NTAPI 4029 NtFlushVirtualMemory(IN HANDLE ProcessHandle, 4030 IN OUT PVOID *BaseAddress, 4031 IN OUT PSIZE_T NumberOfBytesToFlush, 4032 OUT PIO_STATUS_BLOCK IoStatusBlock) 4033 { 4034 PEPROCESS Process; 4035 NTSTATUS Status; 4036 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4037 PVOID CapturedBaseAddress; 4038 SIZE_T CapturedBytesToFlush; 4039 IO_STATUS_BLOCK LocalStatusBlock; 4040 PAGED_CODE(); 4041 4042 // 4043 // Check if we came from user mode 4044 // 4045 if (PreviousMode != KernelMode) 4046 { 4047 // 4048 // Enter SEH for probing 4049 // 4050 _SEH2_TRY 4051 { 4052 // 4053 // Validate all outputs 4054 // 4055 ProbeForWritePointer(BaseAddress); 4056 ProbeForWriteSize_t(NumberOfBytesToFlush); 4057 ProbeForWriteIoStatusBlock(IoStatusBlock); 4058 4059 // 4060 // Capture them 4061 // 4062 CapturedBaseAddress = *BaseAddress; 4063 CapturedBytesToFlush = *NumberOfBytesToFlush; 4064 } 4065 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4066 { 4067 // 4068 // Get exception code 4069 // 4070 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4071 } 4072 _SEH2_END; 4073 } 4074 else 4075 { 4076 // 4077 // Capture directly 4078 // 4079 CapturedBaseAddress = *BaseAddress; 4080 CapturedBytesToFlush = *NumberOfBytesToFlush; 4081 } 4082 4083 // 4084 // Catch illegal base address 4085 // 4086 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 4087 4088 // 4089 // Catch illegal region size 4090 // 4091 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush) 4092 { 4093 // 4094 // Fail 4095 // 4096 return STATUS_INVALID_PARAMETER; 4097 } 4098 4099 // 4100 // Get a reference to the process 4101 // 4102 Status = ObReferenceObjectByHandle(ProcessHandle, 4103 PROCESS_VM_OPERATION, 4104 PsProcessType, 4105 PreviousMode, 4106 (PVOID*)(&Process), 4107 NULL); 4108 if (!NT_SUCCESS(Status)) return Status; 4109 4110 // 4111 // Do it 4112 // 4113 Status = MmFlushVirtualMemory(Process, 4114 &CapturedBaseAddress, 4115 &CapturedBytesToFlush, 4116 &LocalStatusBlock); 4117 4118 // 4119 // Release reference 4120 // 4121 ObDereferenceObject(Process); 4122 4123 // 4124 // Enter SEH to return data 4125 // 4126 _SEH2_TRY 4127 { 4128 // 4129 // Return data to user 4130 // 4131 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); 4132 *NumberOfBytesToFlush = 0; 4133 *IoStatusBlock = LocalStatusBlock; 4134 } 4135 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4136 { 4137 } 4138 _SEH2_END; 4139 4140 // 4141 // Return status 4142 // 4143 return Status; 4144 } 4145 4146 /* 4147 * @unimplemented 4148 */ 4149 NTSTATUS 4150 NTAPI 4151 NtGetWriteWatch(IN HANDLE ProcessHandle, 4152 IN ULONG Flags, 4153 IN PVOID BaseAddress, 4154 IN SIZE_T RegionSize, 4155 IN PVOID *UserAddressArray, 4156 OUT PULONG_PTR EntriesInUserAddressArray, 4157 OUT PULONG Granularity) 4158 { 4159 PEPROCESS Process; 4160 NTSTATUS Status; 4161 PVOID EndAddress; 4162 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4163 ULONG_PTR CapturedEntryCount; 4164 PAGED_CODE(); 4165 4166 // 4167 // Check if we came from user mode 4168 // 4169 if (PreviousMode != KernelMode) 4170 { 4171 // 4172 // Enter SEH for probing 4173 // 4174 _SEH2_TRY 4175 { 4176 // 4177 // Catch illegal base address 4178 // 4179 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2); 4180 4181 // 4182 // Catch illegal region size 4183 // 4184 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 4185 { 4186 // 4187 // Fail 4188 // 4189 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3); 4190 } 4191 4192 // 4193 // Validate all data 4194 // 4195 ProbeForWriteSize_t(EntriesInUserAddressArray); 4196 ProbeForWriteUlong(Granularity); 4197 4198 // 4199 // Capture them 4200 // 4201 CapturedEntryCount = *EntriesInUserAddressArray; 4202 4203 // 4204 // Must have a count 4205 // 4206 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5); 4207 4208 // 4209 // Can't be larger than the maximum 4210 // 4211 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR))) 4212 { 4213 // 4214 // Fail 4215 // 4216 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5); 4217 } 4218 4219 // 4220 // Probe the actual array 4221 // 4222 ProbeForWrite(UserAddressArray, 4223 CapturedEntryCount * sizeof(PVOID), 4224 sizeof(PVOID)); 4225 } 4226 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4227 { 4228 // 4229 // Get exception code 4230 // 4231 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4232 } 4233 _SEH2_END; 4234 } 4235 else 4236 { 4237 // 4238 // Capture directly 4239 // 4240 CapturedEntryCount = *EntriesInUserAddressArray; 4241 ASSERT(CapturedEntryCount != 0); 4242 } 4243 4244 // 4245 // Check if this is a local request 4246 // 4247 if (ProcessHandle == NtCurrentProcess()) 4248 { 4249 // 4250 // No need to reference the process 4251 // 4252 Process = PsGetCurrentProcess(); 4253 } 4254 else 4255 { 4256 // 4257 // Reference the target 4258 // 4259 Status = ObReferenceObjectByHandle(ProcessHandle, 4260 PROCESS_VM_OPERATION, 4261 PsProcessType, 4262 PreviousMode, 4263 (PVOID *)&Process, 4264 NULL); 4265 if (!NT_SUCCESS(Status)) return Status; 4266 } 4267 4268 // 4269 // Compute the last address and validate it 4270 // 4271 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 4272 if (BaseAddress > EndAddress) 4273 { 4274 // 4275 // Fail 4276 // 4277 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4278 return STATUS_INVALID_PARAMETER_4; 4279 } 4280 4281 // 4282 // Oops :( 4283 // 4284 UNIMPLEMENTED; 4285 4286 // 4287 // Dereference if needed 4288 // 4289 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4290 4291 // 4292 // Enter SEH to return data 4293 // 4294 _SEH2_TRY 4295 { 4296 // 4297 // Return data to user 4298 // 4299 *EntriesInUserAddressArray = 0; 4300 *Granularity = PAGE_SIZE; 4301 } 4302 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4303 { 4304 // 4305 // Get exception code 4306 // 4307 Status = _SEH2_GetExceptionCode(); 4308 } 4309 _SEH2_END; 4310 4311 // 4312 // Return success 4313 // 4314 return STATUS_SUCCESS; 4315 } 4316 4317 /* 4318 * @unimplemented 4319 */ 4320 NTSTATUS 4321 NTAPI 4322 NtResetWriteWatch(IN HANDLE ProcessHandle, 4323 IN PVOID BaseAddress, 4324 IN SIZE_T RegionSize) 4325 { 4326 PVOID EndAddress; 4327 PEPROCESS Process; 4328 NTSTATUS Status; 4329 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4330 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); 4331 4332 // 4333 // Catch illegal base address 4334 // 4335 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 4336 4337 // 4338 // Catch illegal region size 4339 // 4340 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 4341 { 4342 // 4343 // Fail 4344 // 4345 return STATUS_INVALID_PARAMETER_3; 4346 } 4347 4348 // 4349 // Check if this is a local request 4350 // 4351 if (ProcessHandle == NtCurrentProcess()) 4352 { 4353 // 4354 // No need to reference the process 4355 // 4356 Process = PsGetCurrentProcess(); 4357 } 4358 else 4359 { 4360 // 4361 // Reference the target 4362 // 4363 Status = ObReferenceObjectByHandle(ProcessHandle, 4364 PROCESS_VM_OPERATION, 4365 PsProcessType, 4366 PreviousMode, 4367 (PVOID *)&Process, 4368 NULL); 4369 if (!NT_SUCCESS(Status)) return Status; 4370 } 4371 4372 // 4373 // Compute the last address and validate it 4374 // 4375 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 4376 if (BaseAddress > EndAddress) 4377 { 4378 // 4379 // Fail 4380 // 4381 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4382 return STATUS_INVALID_PARAMETER_3; 4383 } 4384 4385 // 4386 // Oops :( 4387 // 4388 UNIMPLEMENTED; 4389 4390 // 4391 // Dereference if needed 4392 // 4393 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4394 4395 // 4396 // Return success 4397 // 4398 return STATUS_SUCCESS; 4399 } 4400 4401 NTSTATUS 4402 NTAPI 4403 NtQueryVirtualMemory(IN HANDLE ProcessHandle, 4404 IN PVOID BaseAddress, 4405 IN MEMORY_INFORMATION_CLASS MemoryInformationClass, 4406 OUT PVOID MemoryInformation, 4407 IN SIZE_T MemoryInformationLength, 4408 OUT PSIZE_T ReturnLength) 4409 { 4410 NTSTATUS Status = STATUS_SUCCESS; 4411 KPROCESSOR_MODE PreviousMode; 4412 4413 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress); 4414 4415 /* Bail out if the address is invalid */ 4416 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 4417 4418 /* Probe return buffer */ 4419 PreviousMode = ExGetPreviousMode(); 4420 if (PreviousMode != KernelMode) 4421 { 4422 _SEH2_TRY 4423 { 4424 ProbeForWrite(MemoryInformation, 4425 MemoryInformationLength, 4426 sizeof(ULONG_PTR)); 4427 4428 if (ReturnLength) ProbeForWriteSize_t(ReturnLength); 4429 } 4430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4431 { 4432 Status = _SEH2_GetExceptionCode(); 4433 } 4434 _SEH2_END; 4435 4436 if (!NT_SUCCESS(Status)) 4437 { 4438 return Status; 4439 } 4440 } 4441 4442 switch(MemoryInformationClass) 4443 { 4444 case MemoryBasicInformation: 4445 /* Validate the size information of the class */ 4446 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) 4447 { 4448 /* The size is invalid */ 4449 return STATUS_INFO_LENGTH_MISMATCH; 4450 } 4451 Status = MiQueryMemoryBasicInformation(ProcessHandle, 4452 BaseAddress, 4453 MemoryInformation, 4454 MemoryInformationLength, 4455 ReturnLength); 4456 break; 4457 4458 case MemorySectionName: 4459 /* Validate the size information of the class */ 4460 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME)) 4461 { 4462 /* The size is invalid */ 4463 return STATUS_INFO_LENGTH_MISMATCH; 4464 } 4465 Status = MiQueryMemorySectionName(ProcessHandle, 4466 BaseAddress, 4467 MemoryInformation, 4468 MemoryInformationLength, 4469 ReturnLength); 4470 break; 4471 case MemoryWorkingSetList: 4472 case MemoryBasicVlmInformation: 4473 default: 4474 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass); 4475 break; 4476 } 4477 4478 return Status; 4479 } 4480 4481 /* 4482 * @implemented 4483 */ 4484 NTSTATUS 4485 NTAPI 4486 NtAllocateVirtualMemory(IN HANDLE ProcessHandle, 4487 IN OUT PVOID* UBaseAddress, 4488 IN ULONG_PTR ZeroBits, 4489 IN OUT PSIZE_T URegionSize, 4490 IN ULONG AllocationType, 4491 IN ULONG Protect) 4492 { 4493 PEPROCESS Process; 4494 PMEMORY_AREA MemoryArea; 4495 PMMVAD Vad = NULL, FoundVad; 4496 NTSTATUS Status; 4497 PMMSUPPORT AddressSpace; 4498 PVOID PBaseAddress; 4499 ULONG_PTR PRegionSize, StartingAddress, EndingAddress; 4500 ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS; 4501 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 4502 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 4503 PETHREAD CurrentThread = PsGetCurrentThread(); 4504 KAPC_STATE ApcState; 4505 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0; 4506 BOOLEAN Attached = FALSE, ChangeProtection = FALSE, QuotaCharged = FALSE; 4507 MMPTE TempPte; 4508 PMMPTE PointerPte, LastPte; 4509 PMMPDE PointerPde; 4510 TABLE_SEARCH_RESULT Result; 4511 PAGED_CODE(); 4512 4513 /* Check for valid Zero bits */ 4514 if (ZeroBits > MI_MAX_ZERO_BITS) 4515 { 4516 DPRINT1("Too many zero bits\n"); 4517 return STATUS_INVALID_PARAMETER_3; 4518 } 4519 4520 /* Check for valid Allocation Types */ 4521 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL | 4522 MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES))) 4523 { 4524 DPRINT1("Invalid Allocation Type\n"); 4525 return STATUS_INVALID_PARAMETER_5; 4526 } 4527 4528 /* Check for at least one of these Allocation Types to be set */ 4529 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET))) 4530 { 4531 DPRINT1("No memory allocation base type\n"); 4532 return STATUS_INVALID_PARAMETER_5; 4533 } 4534 4535 /* MEM_RESET is an exclusive flag, make sure that is valid too */ 4536 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET)) 4537 { 4538 DPRINT1("Invalid use of MEM_RESET\n"); 4539 return STATUS_INVALID_PARAMETER_5; 4540 } 4541 4542 /* Check if large pages are being used */ 4543 if (AllocationType & MEM_LARGE_PAGES) 4544 { 4545 /* Large page allocations MUST be committed */ 4546 if (!(AllocationType & MEM_COMMIT)) 4547 { 4548 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n"); 4549 return STATUS_INVALID_PARAMETER_5; 4550 } 4551 4552 /* These flags are not allowed with large page allocations */ 4553 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH)) 4554 { 4555 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n"); 4556 return STATUS_INVALID_PARAMETER_5; 4557 } 4558 } 4559 4560 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */ 4561 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE)) 4562 { 4563 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n"); 4564 return STATUS_INVALID_PARAMETER_5; 4565 } 4566 4567 /* Check for valid MEM_PHYSICAL usage */ 4568 if (AllocationType & MEM_PHYSICAL) 4569 { 4570 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */ 4571 if (!(AllocationType & MEM_RESERVE)) 4572 { 4573 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n"); 4574 return STATUS_INVALID_PARAMETER_5; 4575 } 4576 4577 /* Only these flags are allowed with MEM_PHYSIAL */ 4578 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL)) 4579 { 4580 DPRINT1("Using illegal flags with MEM_PHYSICAL\n"); 4581 return STATUS_INVALID_PARAMETER_5; 4582 } 4583 4584 /* Then make sure PAGE_READWRITE is used */ 4585 if (Protect != PAGE_READWRITE) 4586 { 4587 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n"); 4588 return STATUS_INVALID_PARAMETER_6; 4589 } 4590 } 4591 4592 /* Calculate the protection mask and make sure it's valid */ 4593 ProtectionMask = MiMakeProtectionMask(Protect); 4594 if (ProtectionMask == MM_INVALID_PROTECTION) 4595 { 4596 DPRINT1("Invalid protection mask\n"); 4597 return STATUS_INVALID_PAGE_PROTECTION; 4598 } 4599 4600 /* Enter SEH */ 4601 _SEH2_TRY 4602 { 4603 /* Check for user-mode parameters */ 4604 if (PreviousMode != KernelMode) 4605 { 4606 /* Make sure they are writable */ 4607 ProbeForWritePointer(UBaseAddress); 4608 ProbeForWriteSize_t(URegionSize); 4609 } 4610 4611 /* Capture their values */ 4612 PBaseAddress = *UBaseAddress; 4613 PRegionSize = *URegionSize; 4614 } 4615 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4616 { 4617 /* Return the exception code */ 4618 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4619 } 4620 _SEH2_END; 4621 4622 /* Make sure the allocation isn't past the VAD area */ 4623 if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS) 4624 { 4625 DPRINT1("Virtual allocation base above User Space\n"); 4626 return STATUS_INVALID_PARAMETER_2; 4627 } 4628 4629 /* Make sure the allocation wouldn't overflow past the VAD area */ 4630 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize) 4631 { 4632 DPRINT1("Region size would overflow into kernel-memory\n"); 4633 return STATUS_INVALID_PARAMETER_4; 4634 } 4635 4636 /* Make sure there's a size specified */ 4637 if (!PRegionSize) 4638 { 4639 DPRINT1("Region size is invalid (zero)\n"); 4640 return STATUS_INVALID_PARAMETER_4; 4641 } 4642 4643 // 4644 // If this is for the current process, just use PsGetCurrentProcess 4645 // 4646 if (ProcessHandle == NtCurrentProcess()) 4647 { 4648 Process = CurrentProcess; 4649 } 4650 else 4651 { 4652 // 4653 // Otherwise, reference the process with VM rights and attach to it if 4654 // this isn't the current process. We must attach because we'll be touching 4655 // PTEs and PDEs that belong to user-mode memory, and also touching the 4656 // Working Set which is stored in Hyperspace. 4657 // 4658 Status = ObReferenceObjectByHandle(ProcessHandle, 4659 PROCESS_VM_OPERATION, 4660 PsProcessType, 4661 PreviousMode, 4662 (PVOID*)&Process, 4663 NULL); 4664 if (!NT_SUCCESS(Status)) return Status; 4665 if (CurrentProcess != Process) 4666 { 4667 KeStackAttachProcess(&Process->Pcb, &ApcState); 4668 Attached = TRUE; 4669 } 4670 } 4671 4672 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n", 4673 Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect); 4674 4675 // 4676 // Check for large page allocations and make sure that the required privilege 4677 // is being held, before attempting to handle them. 4678 // 4679 if ((AllocationType & MEM_LARGE_PAGES) && 4680 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))) 4681 { 4682 /* Fail without it */ 4683 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n"); 4684 Status = STATUS_PRIVILEGE_NOT_HELD; 4685 goto FailPathNoLock; 4686 } 4687 4688 // 4689 // Fail on the things we don't yet support 4690 // 4691 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES) 4692 { 4693 DPRINT1("MEM_LARGE_PAGES not supported\n"); 4694 Status = STATUS_INVALID_PARAMETER; 4695 goto FailPathNoLock; 4696 } 4697 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL) 4698 { 4699 DPRINT1("MEM_PHYSICAL not supported\n"); 4700 Status = STATUS_INVALID_PARAMETER; 4701 goto FailPathNoLock; 4702 } 4703 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH) 4704 { 4705 DPRINT1("MEM_WRITE_WATCH not supported\n"); 4706 Status = STATUS_INVALID_PARAMETER; 4707 goto FailPathNoLock; 4708 } 4709 4710 // 4711 // Check if the caller is reserving memory, or committing memory and letting 4712 // us pick the base address 4713 // 4714 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE)) 4715 { 4716 // 4717 // Do not allow COPY_ON_WRITE through this API 4718 // 4719 if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) 4720 { 4721 DPRINT1("Copy on write not allowed through this path\n"); 4722 Status = STATUS_INVALID_PAGE_PROTECTION; 4723 goto FailPathNoLock; 4724 } 4725 4726 // 4727 // Does the caller have an address in mind, or is this a blind commit? 4728 // 4729 if (!PBaseAddress) 4730 { 4731 // 4732 // This is a blind commit, all we need is the region size 4733 // 4734 PRegionSize = ROUND_TO_PAGES(PRegionSize); 4735 EndingAddress = 0; 4736 StartingAddress = 0; 4737 4738 // 4739 // Check if ZeroBits were specified 4740 // 4741 if (ZeroBits != 0) 4742 { 4743 // 4744 // Calculate the highest address and check if it's valid 4745 // 4746 HighestAddress = MAXULONG_PTR >> ZeroBits; 4747 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) 4748 { 4749 Status = STATUS_INVALID_PARAMETER_3; 4750 goto FailPathNoLock; 4751 } 4752 } 4753 } 4754 else 4755 { 4756 // 4757 // This is a reservation, so compute the starting address on the 4758 // expected 64KB granularity, and see where the ending address will 4759 // fall based on the aligned address and the passed in region size 4760 // 4761 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 4762 PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K); 4763 StartingAddress = (ULONG_PTR)PBaseAddress; 4764 } 4765 4766 // Charge quotas for the VAD 4767 Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 4768 if (!NT_SUCCESS(Status)) 4769 { 4770 DPRINT1("Quota exceeded.\n"); 4771 goto FailPathNoLock; 4772 } 4773 4774 QuotaCharged = TRUE; 4775 4776 // 4777 // Allocate and initialize the VAD 4778 // 4779 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); 4780 if (Vad == NULL) 4781 { 4782 DPRINT1("Failed to allocate a VAD!\n"); 4783 Status = STATUS_INSUFFICIENT_RESOURCES; 4784 goto FailPathNoLock; 4785 } 4786 4787 RtlZeroMemory(Vad, sizeof(MMVAD_LONG)); 4788 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1; 4789 Vad->u.VadFlags.Protection = ProtectionMask; 4790 Vad->u.VadFlags.PrivateMemory = 1; 4791 Vad->ControlArea = NULL; // For Memory-Area hack 4792 4793 // 4794 // Insert the VAD 4795 // 4796 Status = MiInsertVadEx(Vad, 4797 &StartingAddress, 4798 PRegionSize, 4799 HighestAddress, 4800 MM_VIRTMEM_GRANULARITY, 4801 AllocationType); 4802 if (!NT_SUCCESS(Status)) 4803 { 4804 DPRINT1("Failed to insert the VAD!\n"); 4805 ExFreePoolWithTag(Vad, 'SdaV'); 4806 goto FailPathNoLock; 4807 } 4808 4809 // 4810 // Detach and dereference the target process if 4811 // it was different from the current process 4812 // 4813 if (Attached) KeUnstackDetachProcess(&ApcState); 4814 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4815 4816 // 4817 // Use SEH to write back the base address and the region size. In the case 4818 // of an exception, we do not return back the exception code, as the memory 4819 // *has* been allocated. The caller would now have to call VirtualQuery 4820 // or do some other similar trick to actually find out where its memory 4821 // allocation ended up 4822 // 4823 _SEH2_TRY 4824 { 4825 *URegionSize = PRegionSize; 4826 *UBaseAddress = (PVOID)StartingAddress; 4827 } 4828 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4829 { 4830 // 4831 // Ignore exception! 4832 // 4833 } 4834 _SEH2_END; 4835 DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress); 4836 return STATUS_SUCCESS; 4837 } 4838 4839 // 4840 // This is a MEM_COMMIT on top of an existing address which must have been 4841 // MEM_RESERVED already. Compute the start and ending base addresses based 4842 // on the user input, and then compute the actual region size once all the 4843 // alignments have been done. 4844 // 4845 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1)); 4846 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 4847 PRegionSize = EndingAddress - StartingAddress + 1; 4848 4849 // 4850 // Lock the address space and make sure the process isn't already dead 4851 // 4852 AddressSpace = MmGetCurrentAddressSpace(); 4853 MmLockAddressSpace(AddressSpace); 4854 if (Process->VmDeleted) 4855 { 4856 DPRINT1("Process is dying\n"); 4857 Status = STATUS_PROCESS_IS_TERMINATING; 4858 goto FailPath; 4859 } 4860 4861 // 4862 // Get the VAD for this address range, and make sure it exists 4863 // 4864 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 4865 EndingAddress >> PAGE_SHIFT, 4866 &Process->VadRoot, 4867 (PMMADDRESS_NODE*)&FoundVad); 4868 if (Result != TableFoundNode) 4869 { 4870 DPRINT1("Could not find a VAD for this allocation\n"); 4871 Status = STATUS_CONFLICTING_ADDRESSES; 4872 goto FailPath; 4873 } 4874 4875 if ((AllocationType & MEM_RESET) == MEM_RESET) 4876 { 4877 /// @todo HACK: pretend success 4878 DPRINT("MEM_RESET not supported\n"); 4879 Status = STATUS_SUCCESS; 4880 goto FailPath; 4881 } 4882 4883 // 4884 // These kinds of VADs are illegal for this Windows function when trying to 4885 // commit an existing range 4886 // 4887 if ((FoundVad->u.VadFlags.VadType == VadAwe) || 4888 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 4889 (FoundVad->u.VadFlags.VadType == VadLargePages)) 4890 { 4891 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n"); 4892 Status = STATUS_CONFLICTING_ADDRESSES; 4893 goto FailPath; 4894 } 4895 4896 // 4897 // Make sure that this address range actually fits within the VAD for it 4898 // 4899 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) || 4900 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn)) 4901 { 4902 DPRINT1("Address range does not fit into the VAD\n"); 4903 Status = STATUS_CONFLICTING_ADDRESSES; 4904 goto FailPath; 4905 } 4906 4907 // 4908 // Make sure this is an ARM3 section 4909 // 4910 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress)); 4911 ASSERT(MemoryArea != NULL); 4912 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3) 4913 { 4914 DPRINT1("Illegal commit of non-ARM3 section!\n"); 4915 Status = STATUS_ALREADY_COMMITTED; 4916 goto FailPath; 4917 } 4918 4919 // Is this a previously reserved section being committed? If so, enter the 4920 // special section path 4921 // 4922 if (FoundVad->u.VadFlags.PrivateMemory == FALSE) 4923 { 4924 // 4925 // You cannot commit large page sections through this API 4926 // 4927 if (FoundVad->u.VadFlags.VadType == VadLargePageSection) 4928 { 4929 DPRINT1("Large page sections cannot be VirtualAlloc'd\n"); 4930 Status = STATUS_INVALID_PAGE_PROTECTION; 4931 goto FailPath; 4932 } 4933 4934 // 4935 // You can only use caching flags on a rotate VAD 4936 // 4937 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) && 4938 (FoundVad->u.VadFlags.VadType != VadRotatePhysical)) 4939 { 4940 DPRINT1("Cannot use caching flags with anything but rotate VADs\n"); 4941 Status = STATUS_INVALID_PAGE_PROTECTION; 4942 goto FailPath; 4943 } 4944 4945 // 4946 // We should make sure that the section's permissions aren't being 4947 // messed with 4948 // 4949 if (FoundVad->u.VadFlags.NoChange) 4950 { 4951 // 4952 // Make sure it's okay to touch it 4953 // Note: The Windows 2003 kernel has a bug here, passing the 4954 // unaligned base address together with the aligned size, 4955 // potentially covering a region larger than the actual allocation. 4956 // Might be exposed through NtGdiCreateDIBSection w/ section handle 4957 // For now we keep this behavior. 4958 // TODO: analyze possible implications, create test case 4959 // 4960 Status = MiCheckSecuredVad(FoundVad, 4961 PBaseAddress, 4962 PRegionSize, 4963 ProtectionMask); 4964 if (!NT_SUCCESS(Status)) 4965 { 4966 DPRINT1("Secured VAD being messed around with\n"); 4967 goto FailPath; 4968 } 4969 } 4970 4971 // 4972 // ARM3 does not support file-backed sections, only shared memory 4973 // 4974 ASSERT(FoundVad->ControlArea->FilePointer == NULL); 4975 4976 // 4977 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write 4978 // 4979 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) && 4980 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD))) 4981 { 4982 DPRINT1("Invalid page protection for rotate VAD\n"); 4983 Status = STATUS_INVALID_PAGE_PROTECTION; 4984 goto FailPath; 4985 } 4986 4987 // 4988 // Compute PTE addresses and the quota charge, then grab the commit lock 4989 // 4990 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT); 4991 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT); 4992 QuotaCharge = (ULONG)(LastPte - PointerPte + 1); 4993 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); 4994 4995 // 4996 // Get the segment template PTE and start looping each page 4997 // 4998 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate; 4999 ASSERT(TempPte.u.Long != 0); 5000 while (PointerPte <= LastPte) 5001 { 5002 // 5003 // For each non-already-committed page, write the invalid template PTE 5004 // 5005 if (PointerPte->u.Long == 0) 5006 { 5007 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5008 } 5009 else 5010 { 5011 QuotaFree++; 5012 } 5013 PointerPte++; 5014 } 5015 5016 // 5017 // Now do the commit accounting and release the lock 5018 // 5019 ASSERT(QuotaCharge >= QuotaFree); 5020 QuotaCharge -= QuotaFree; 5021 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge; 5022 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 5023 5024 // 5025 // We are done with committing the section pages 5026 // 5027 Status = STATUS_SUCCESS; 5028 goto FailPath; 5029 } 5030 5031 // 5032 // This is a specific ReactOS check because we only use normal VADs 5033 // 5034 ASSERT(FoundVad->u.VadFlags.VadType == VadNone); 5035 5036 // 5037 // While this is an actual Windows check 5038 // 5039 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical); 5040 5041 // 5042 // Throw out attempts to use copy-on-write through this API path 5043 // 5044 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)) 5045 { 5046 DPRINT1("Write copy attempted when not allowed\n"); 5047 Status = STATUS_INVALID_PAGE_PROTECTION; 5048 goto FailPath; 5049 } 5050 5051 // 5052 // Initialize a demand-zero PTE 5053 // 5054 TempPte.u.Long = 0; 5055 TempPte.u.Soft.Protection = ProtectionMask; 5056 ASSERT(TempPte.u.Long != 0); 5057 5058 // 5059 // Get the PTE, PDE and the last PTE for this address range 5060 // 5061 PointerPde = MiAddressToPde(StartingAddress); 5062 PointerPte = MiAddressToPte(StartingAddress); 5063 LastPte = MiAddressToPte(EndingAddress); 5064 5065 // 5066 // Update the commit charge in the VAD as well as in the process, and check 5067 // if this commit charge was now higher than the last recorded peak, in which 5068 // case we also update the peak 5069 // 5070 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte); 5071 Process->CommitCharge += (1 + LastPte - PointerPte); 5072 if (Process->CommitCharge > Process->CommitChargePeak) 5073 { 5074 Process->CommitChargePeak = Process->CommitCharge; 5075 } 5076 5077 // 5078 // Lock the working set while we play with user pages and page tables 5079 // 5080 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5081 5082 // 5083 // Make the current page table valid, and then loop each page within it 5084 // 5085 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 5086 while (PointerPte <= LastPte) 5087 { 5088 // 5089 // Have we crossed into a new page table? 5090 // 5091 if (MiIsPteOnPdeBoundary(PointerPte)) 5092 { 5093 // 5094 // Get the PDE and now make it valid too 5095 // 5096 PointerPde = MiPteToPde(PointerPte); 5097 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 5098 } 5099 5100 // 5101 // Is this a zero PTE as expected? 5102 // 5103 if (PointerPte->u.Long == 0) 5104 { 5105 // 5106 // First increment the count of pages in the page table for this 5107 // process 5108 // 5109 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 5110 5111 // 5112 // And now write the invalid demand-zero PTE as requested 5113 // 5114 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5115 } 5116 else if (PointerPte->u.Long == MmDecommittedPte.u.Long) 5117 { 5118 // 5119 // If the PTE was already decommitted, there is nothing else to do 5120 // but to write the new demand-zero PTE 5121 // 5122 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5123 } 5124 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte))) 5125 { 5126 // 5127 // We don't handle these scenarios yet 5128 // 5129 if (PointerPte->u.Soft.Valid == 0) 5130 { 5131 ASSERT(PointerPte->u.Soft.Prototype == 0); 5132 ASSERT((PointerPte->u.Soft.PageFileHigh == 0) || (PointerPte->u.Soft.Transition == 1)); 5133 } 5134 5135 // 5136 // There's a change in protection, remember this for later, but do 5137 // not yet handle it. 5138 // 5139 ChangeProtection = TRUE; 5140 } 5141 5142 // 5143 // Move to the next PTE 5144 // 5145 PointerPte++; 5146 } 5147 5148 // 5149 // Release the working set lock, unlock the address space, and detach from 5150 // the target process if it was not the current process. Also dereference the 5151 // target process if this wasn't the case. 5152 // 5153 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 5154 Status = STATUS_SUCCESS; 5155 FailPath: 5156 MmUnlockAddressSpace(AddressSpace); 5157 5158 if (!NT_SUCCESS(Status)) 5159 { 5160 if (Vad != NULL) 5161 { 5162 ExFreePoolWithTag(Vad, 'SdaV'); 5163 } 5164 } 5165 5166 // 5167 // Check if we need to update the protection 5168 // 5169 if (ChangeProtection) 5170 { 5171 PVOID ProtectBaseAddress = (PVOID)StartingAddress; 5172 SIZE_T ProtectSize = PRegionSize; 5173 ULONG OldProtection; 5174 5175 // 5176 // Change the protection of the region 5177 // 5178 MiProtectVirtualMemory(Process, 5179 &ProtectBaseAddress, 5180 &ProtectSize, 5181 Protect, 5182 &OldProtection); 5183 } 5184 5185 FailPathNoLock: 5186 if (Attached) KeUnstackDetachProcess(&ApcState); 5187 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5188 5189 // 5190 // Only write back results on success 5191 // 5192 if (NT_SUCCESS(Status)) 5193 { 5194 // 5195 // Use SEH to write back the base address and the region size. In the case 5196 // of an exception, we strangely do return back the exception code, even 5197 // though the memory *has* been allocated. This mimics Windows behavior and 5198 // there is not much we can do about it. 5199 // 5200 _SEH2_TRY 5201 { 5202 *URegionSize = PRegionSize; 5203 *UBaseAddress = (PVOID)StartingAddress; 5204 } 5205 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5206 { 5207 Status = _SEH2_GetExceptionCode(); 5208 } 5209 _SEH2_END; 5210 } 5211 else if (QuotaCharged) 5212 { 5213 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5214 } 5215 5216 return Status; 5217 } 5218 5219 /* 5220 * @implemented 5221 */ 5222 NTSTATUS 5223 NTAPI 5224 NtFreeVirtualMemory(IN HANDLE ProcessHandle, 5225 IN PVOID* UBaseAddress, 5226 IN PSIZE_T URegionSize, 5227 IN ULONG FreeType) 5228 { 5229 PMEMORY_AREA MemoryArea; 5230 SIZE_T PRegionSize; 5231 PVOID PBaseAddress; 5232 LONG_PTR AlreadyDecommitted, CommitReduction = 0; 5233 LONG_PTR FirstCommit; 5234 ULONG_PTR StartingAddress, EndingAddress; 5235 PMMVAD Vad; 5236 PMMVAD NewVad; 5237 NTSTATUS Status; 5238 PEPROCESS Process; 5239 PMMSUPPORT AddressSpace; 5240 PETHREAD CurrentThread = PsGetCurrentThread(); 5241 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 5242 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 5243 KAPC_STATE ApcState; 5244 BOOLEAN Attached = FALSE; 5245 PAGED_CODE(); 5246 5247 // 5248 // Only two flags are supported, exclusively. 5249 // 5250 if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT) 5251 { 5252 DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType); 5253 return STATUS_INVALID_PARAMETER_4; 5254 } 5255 5256 // 5257 // Enter SEH for probe and capture. On failure, return back to the caller 5258 // with an exception violation. 5259 // 5260 _SEH2_TRY 5261 { 5262 // 5263 // Check for user-mode parameters and make sure that they are writeable 5264 // 5265 if (PreviousMode != KernelMode) 5266 { 5267 ProbeForWritePointer(UBaseAddress); 5268 ProbeForWriteUlong(URegionSize); 5269 } 5270 5271 // 5272 // Capture the current values 5273 // 5274 PBaseAddress = *UBaseAddress; 5275 PRegionSize = *URegionSize; 5276 } 5277 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5278 { 5279 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 5280 } 5281 _SEH2_END; 5282 5283 // 5284 // Make sure the allocation isn't past the user area 5285 // 5286 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS) 5287 { 5288 DPRINT1("Virtual free base above User Space\n"); 5289 return STATUS_INVALID_PARAMETER_2; 5290 } 5291 5292 // 5293 // Make sure the allocation wouldn't overflow past the user area 5294 // 5295 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize) 5296 { 5297 DPRINT1("Region size would overflow into kernel-memory\n"); 5298 return STATUS_INVALID_PARAMETER_3; 5299 } 5300 5301 // 5302 // If this is for the current process, just use PsGetCurrentProcess 5303 // 5304 if (ProcessHandle == NtCurrentProcess()) 5305 { 5306 Process = CurrentProcess; 5307 } 5308 else 5309 { 5310 // 5311 // Otherwise, reference the process with VM rights and attach to it if 5312 // this isn't the current process. We must attach because we'll be touching 5313 // PTEs and PDEs that belong to user-mode memory, and also touching the 5314 // Working Set which is stored in Hyperspace. 5315 // 5316 Status = ObReferenceObjectByHandle(ProcessHandle, 5317 PROCESS_VM_OPERATION, 5318 PsProcessType, 5319 PreviousMode, 5320 (PVOID*)&Process, 5321 NULL); 5322 if (!NT_SUCCESS(Status)) return Status; 5323 if (CurrentProcess != Process) 5324 { 5325 KeStackAttachProcess(&Process->Pcb, &ApcState); 5326 Attached = TRUE; 5327 } 5328 } 5329 5330 DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n", 5331 Process, PBaseAddress, PRegionSize, FreeType); 5332 5333 // 5334 // Lock the address space 5335 // 5336 AddressSpace = MmGetCurrentAddressSpace(); 5337 MmLockAddressSpace(AddressSpace); 5338 5339 // 5340 // If the address space is being deleted, fail the de-allocation since it's 5341 // too late to do anything about it 5342 // 5343 if (Process->VmDeleted) 5344 { 5345 DPRINT1("Process is dead\n"); 5346 Status = STATUS_PROCESS_IS_TERMINATING; 5347 goto FailPath; 5348 } 5349 5350 // 5351 // Compute start and end addresses, and locate the VAD 5352 // 5353 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 5354 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 5355 Vad = MiLocateAddress((PVOID)StartingAddress); 5356 if (!Vad) 5357 { 5358 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress); 5359 Status = STATUS_MEMORY_NOT_ALLOCATED; 5360 goto FailPath; 5361 } 5362 5363 // 5364 // If the range exceeds the VAD's ending VPN, fail this request 5365 // 5366 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT)) 5367 { 5368 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress); 5369 Status = STATUS_UNABLE_TO_FREE_VM; 5370 goto FailPath; 5371 } 5372 5373 // 5374 // Only private memory (except rotate VADs) can be freed through here */ 5375 // 5376 if ((!(Vad->u.VadFlags.PrivateMemory) && 5377 (Vad->u.VadFlags.VadType != VadRotatePhysical)) || 5378 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory)) 5379 { 5380 DPRINT("Attempt to free section memory\n"); 5381 Status = STATUS_UNABLE_TO_DELETE_SECTION; 5382 goto FailPath; 5383 } 5384 5385 // 5386 // ARM3 does not yet handle protected VM 5387 // 5388 ASSERT(Vad->u.VadFlags.NoChange == 0); 5389 5390 // 5391 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation 5392 // and that is is an ARM3 memory area, and not a section view, as we currently 5393 // don't support freeing those though this interface. 5394 // 5395 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress); 5396 ASSERT(MemoryArea); 5397 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3); 5398 5399 // 5400 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT 5401 // 5402 if (FreeType & MEM_RELEASE) 5403 { 5404 // 5405 // ARM3 only supports this VAD in this path 5406 // 5407 ASSERT(Vad->u.VadFlags.VadType == VadNone); 5408 5409 // 5410 // Is the caller trying to remove the whole VAD, or remove only a portion 5411 // of it? If no region size is specified, then the assumption is that the 5412 // whole VAD is to be destroyed 5413 // 5414 if (!PRegionSize) 5415 { 5416 // 5417 // The caller must specify the base address identically to the range 5418 // that is stored in the VAD. 5419 // 5420 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 5421 { 5422 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress); 5423 Status = STATUS_FREE_VM_NOT_AT_BASE; 5424 goto FailPath; 5425 } 5426 5427 // 5428 // Now compute the actual start/end addresses based on the VAD 5429 // 5430 StartingAddress = Vad->StartingVpn << PAGE_SHIFT; 5431 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 5432 5433 // 5434 // Finally lock the working set and remove the VAD from the VAD tree 5435 // 5436 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5437 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 5438 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 5439 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5440 } 5441 else 5442 { 5443 // 5444 // This means the caller wants to release a specific region within 5445 // the range. We have to find out which range this is -- the following 5446 // possibilities exist plus their union (CASE D): 5447 // 5448 // STARTING ADDRESS ENDING ADDRESS 5449 // [<========][========================================][=========>] 5450 // CASE A CASE B CASE C 5451 // 5452 // 5453 // First, check for case A or D 5454 // 5455 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn) 5456 { 5457 // 5458 // Check for case D 5459 // 5460 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 5461 { 5462 // 5463 // Case D (freeing the entire region) 5464 // 5465 // This is the easiest one to handle -- it is identical to 5466 // the code path above when the caller sets a zero region size 5467 // and the whole VAD is destroyed 5468 // 5469 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5470 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 5471 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 5472 PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5473 } 5474 else 5475 { 5476 // 5477 // Case A (freeing a part at the beginning) 5478 // 5479 // This case is pretty easy too -- we compute a bunch of 5480 // pages to decommit, and then push the VAD's starting address 5481 // a bit further down, then decrement the commit charge 5482 // 5483 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5484 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5485 EndingAddress, 5486 Vad, 5487 Process); 5488 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5489 // For ReactOS: shrink the corresponding memory area 5490 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5491 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5492 Vad->StartingVpn = (EndingAddress + 1) >> PAGE_SHIFT; 5493 MemoryArea->VadNode.StartingVpn = Vad->StartingVpn; 5494 5495 // 5496 // After analyzing the VAD, set it to NULL so that we don't 5497 // free it in the exit path 5498 // 5499 Vad = NULL; 5500 } 5501 } 5502 else 5503 { 5504 // 5505 // This is case B or case C. First check for case C 5506 // 5507 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 5508 { 5509 // 5510 // Case C (freeing a part at the end) 5511 // 5512 // This is pretty easy and similar to case A. We compute the 5513 // amount of pages to decommit, update the VAD's commit charge 5514 // and then change the ending address of the VAD to be a bit 5515 // smaller. 5516 // 5517 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5518 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5519 EndingAddress, 5520 Vad, 5521 Process); 5522 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5523 // For ReactOS: shrink the corresponding memory area 5524 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5525 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5526 Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT; 5527 MemoryArea->VadNode.EndingVpn = Vad->EndingVpn; 5528 } 5529 else 5530 { 5531 // 5532 // Case B (freeing a part in the middle) 5533 // 5534 // This is the hardest one. Because we are removing a chunk 5535 // of memory from the very middle of the VAD, we must actually 5536 // split the VAD into two new VADs and compute the commit 5537 // charges for each of them, and reinsert new charges. 5538 // 5539 NewVad = ExAllocatePoolZero(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); 5540 if (NewVad == NULL) 5541 { 5542 DPRINT1("Failed to allocate a VAD!\n"); 5543 Status = STATUS_INSUFFICIENT_RESOURCES; 5544 goto FailPath; 5545 } 5546 5547 // Charge quota for the new VAD 5548 Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG)); 5549 5550 if (!NT_SUCCESS(Status)) 5551 { 5552 DPRINT1("Ran out of process quota whilst creating new VAD!\n"); 5553 ExFreePoolWithTag(NewVad, 'SdaV'); 5554 Status = STATUS_QUOTA_EXCEEDED; 5555 goto FailPath; 5556 } 5557 5558 // 5559 // This new VAD describes the second chunk, so we keep the end 5560 // address of the original and adjust the start to point past 5561 // the released region. 5562 // The commit charge will be calculated below. 5563 // 5564 NewVad->StartingVpn = (EndingAddress + 1) >> PAGE_SHIFT; 5565 NewVad->EndingVpn = Vad->EndingVpn; 5566 NewVad->u.LongFlags = Vad->u.LongFlags; 5567 NewVad->u.VadFlags.CommitCharge = 0; 5568 ASSERT(NewVad->EndingVpn >= NewVad->StartingVpn); 5569 5570 // 5571 // Get the commit charge for the released region 5572 // 5573 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5574 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5575 EndingAddress, 5576 Vad, 5577 Process); 5578 5579 // 5580 // Adjust the end of the original VAD (first chunk). 5581 // For ReactOS: shrink the corresponding memory area 5582 // 5583 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5584 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5585 Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT; 5586 MemoryArea->VadNode.EndingVpn = Vad->EndingVpn; 5587 5588 // 5589 // Now the addresses for both VADs are consistent, 5590 // so insert the new one. 5591 // ReactOS: This will take care of creating a second MEMORY_AREA. 5592 // 5593 MiInsertVad(NewVad, &Process->VadRoot); 5594 5595 // 5596 // Calculate the commit charge for the first split. 5597 // The second chunk's size is the original size, minus the 5598 // released region's size, minus this first chunk. 5599 // 5600 FirstCommit = MiCalculatePageCommitment(Vad->StartingVpn << PAGE_SHIFT, 5601 StartingAddress - 1, 5602 Vad, 5603 Process); 5604 NewVad->u.VadFlags.CommitCharge = Vad->u.VadFlags.CommitCharge - CommitReduction - FirstCommit; 5605 Vad->u.VadFlags.CommitCharge = FirstCommit; 5606 } 5607 5608 // 5609 // After analyzing the VAD, set it to NULL so that we don't 5610 // free it in the exit path 5611 // 5612 Vad = NULL; 5613 } 5614 } 5615 5616 // 5617 // Now we have a range of pages to dereference, so call the right API 5618 // to do that and then release the working set, since we're done messing 5619 // around with process pages. 5620 // 5621 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL); 5622 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 5623 Status = STATUS_SUCCESS; 5624 5625 FinalPath: 5626 // 5627 // Update the process counters 5628 // 5629 PRegionSize = EndingAddress - StartingAddress + 1; 5630 Process->CommitCharge -= CommitReduction; 5631 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize; 5632 5633 // 5634 // Unlock the address space and free the VAD in failure cases. Next, 5635 // detach from the target process so we can write the region size and the 5636 // base address to the correct source process, and dereference the target 5637 // process. 5638 // 5639 MmUnlockAddressSpace(AddressSpace); 5640 if (Vad) ExFreePool(Vad); 5641 if (Attached) KeUnstackDetachProcess(&ApcState); 5642 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5643 5644 // 5645 // Use SEH to safely return the region size and the base address of the 5646 // deallocation. If we get an access violation, don't return a failure code 5647 // as the deallocation *has* happened. The caller will just have to figure 5648 // out another way to find out where it is (such as VirtualQuery). 5649 // 5650 _SEH2_TRY 5651 { 5652 *URegionSize = PRegionSize; 5653 *UBaseAddress = (PVOID)StartingAddress; 5654 } 5655 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5656 { 5657 } 5658 _SEH2_END; 5659 return Status; 5660 } 5661 5662 // 5663 // This is the decommit path. You cannot decommit from the following VADs in 5664 // Windows, so fail the vall 5665 // 5666 if ((Vad->u.VadFlags.VadType == VadAwe) || 5667 (Vad->u.VadFlags.VadType == VadLargePages) || 5668 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 5669 { 5670 DPRINT1("Trying to decommit from invalid VAD\n"); 5671 Status = STATUS_MEMORY_NOT_ALLOCATED; 5672 goto FailPath; 5673 } 5674 5675 // 5676 // If the caller did not specify a region size, first make sure that this 5677 // region is actually committed. If it is, then compute the ending address 5678 // based on the VAD. 5679 // 5680 if (!PRegionSize) 5681 { 5682 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 5683 { 5684 DPRINT1("Decomitting non-committed memory\n"); 5685 Status = STATUS_FREE_VM_NOT_AT_BASE; 5686 goto FailPath; 5687 } 5688 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 5689 } 5690 5691 // 5692 // Decommit the PTEs for the range plus the actual backing pages for the 5693 // range, then reduce that amount from the commit charge in the VAD 5694 // 5695 AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress, 5696 MiAddressToPte(EndingAddress), 5697 Process, 5698 Vad); 5699 CommitReduction = MiAddressToPte(EndingAddress) - 5700 MiAddressToPte(StartingAddress) + 5701 1 - 5702 AlreadyDecommitted; 5703 5704 ASSERT(CommitReduction >= 0); 5705 ASSERT(Vad->u.VadFlags.CommitCharge >= CommitReduction); 5706 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5707 5708 // 5709 // We are done, go to the exit path without freeing the VAD as it remains 5710 // valid since we have not released the allocation. 5711 // 5712 Vad = NULL; 5713 Status = STATUS_SUCCESS; 5714 goto FinalPath; 5715 5716 // 5717 // In the failure path, we detach and dereference the target process, and 5718 // return whatever failure code was sent. 5719 // 5720 FailPath: 5721 MmUnlockAddressSpace(AddressSpace); 5722 if (Attached) KeUnstackDetachProcess(&ApcState); 5723 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5724 return Status; 5725 } 5726 5727 5728 PHYSICAL_ADDRESS 5729 NTAPI 5730 MmGetPhysicalAddress(PVOID Address) 5731 { 5732 PHYSICAL_ADDRESS PhysicalAddress; 5733 MMPDE TempPde; 5734 MMPTE TempPte; 5735 5736 /* Check if the PXE/PPE/PDE is valid */ 5737 if ( 5738 #if (_MI_PAGING_LEVELS == 4) 5739 (MiAddressToPxe(Address)->u.Hard.Valid) && 5740 #endif 5741 #if (_MI_PAGING_LEVELS >= 3) 5742 (MiAddressToPpe(Address)->u.Hard.Valid) && 5743 #endif 5744 (MiAddressToPde(Address)->u.Hard.Valid)) 5745 { 5746 /* Check for large pages */ 5747 TempPde = *MiAddressToPde(Address); 5748 if (TempPde.u.Hard.LargePage) 5749 { 5750 /* Physical address is base page + large page offset */ 5751 PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT; 5752 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1)); 5753 return PhysicalAddress; 5754 } 5755 5756 /* Check if the PTE is valid */ 5757 TempPte = *MiAddressToPte(Address); 5758 if (TempPte.u.Hard.Valid) 5759 { 5760 /* Physical address is base page + page offset */ 5761 PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT; 5762 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1)); 5763 return PhysicalAddress; 5764 } 5765 } 5766 5767 KeRosDumpStackFrames(NULL, 20); 5768 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address); 5769 PhysicalAddress.QuadPart = 0; 5770 return PhysicalAddress; 5771 } 5772 5773 5774 /* EOF */ 5775