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