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 /* For now we call the old Mm */ 1360 return MmRosFlushVirtualMemory(Process, BaseAddress, RegionSize, IoStatusBlock); 1361 } 1362 1363 ULONG 1364 NTAPI 1365 MiGetPageProtection(IN PMMPTE PointerPte) 1366 { 1367 MMPTE TempPte; 1368 PMMPFN Pfn; 1369 PEPROCESS CurrentProcess; 1370 PETHREAD CurrentThread; 1371 BOOLEAN WsSafe, WsShared; 1372 ULONG Protect; 1373 KIRQL OldIrql; 1374 PAGED_CODE(); 1375 1376 /* Copy this PTE's contents */ 1377 TempPte = *PointerPte; 1378 1379 /* Assure it's not totally zero */ 1380 ASSERT(TempPte.u.Long); 1381 1382 /* Check for a special prototype format */ 1383 if ((TempPte.u.Soft.Valid == 0) && 1384 (TempPte.u.Soft.Prototype == 1)) 1385 { 1386 /* Check if the prototype PTE is not yet pointing to a PTE */ 1387 if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) 1388 { 1389 /* The prototype PTE contains the protection */ 1390 return MmProtectToValue[TempPte.u.Soft.Protection]; 1391 } 1392 1393 /* Get a pointer to the underlying shared PTE */ 1394 PointerPte = MiProtoPteToPte(&TempPte); 1395 1396 /* Since the PTE we want to read can be paged out at any time, we need 1397 to release the working set lock first, so that it can be paged in */ 1398 CurrentThread = PsGetCurrentThread(); 1399 CurrentProcess = PsGetCurrentProcess(); 1400 MiUnlockProcessWorkingSetForFault(CurrentProcess, 1401 CurrentThread, 1402 &WsSafe, 1403 &WsShared); 1404 1405 /* Now read the PTE value */ 1406 TempPte = *PointerPte; 1407 1408 /* Check if that one is invalid */ 1409 if (!TempPte.u.Hard.Valid) 1410 { 1411 /* We get the protection directly from this PTE */ 1412 Protect = MmProtectToValue[TempPte.u.Soft.Protection]; 1413 } 1414 else 1415 { 1416 /* The PTE is valid, so we might need to get the protection from 1417 the PFN. Lock the PFN database */ 1418 OldIrql = MiAcquirePfnLock(); 1419 1420 /* Check if the PDE is still valid */ 1421 if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0) 1422 { 1423 /* It's not, make it valid */ 1424 MiMakeSystemAddressValidPfn(PointerPte, OldIrql); 1425 } 1426 1427 /* Now it's safe to read the PTE value again */ 1428 TempPte = *PointerPte; 1429 ASSERT(TempPte.u.Long != 0); 1430 1431 /* Check again if the PTE is invalid */ 1432 if (!TempPte.u.Hard.Valid) 1433 { 1434 /* The PTE is not valid, so we can use it's protection field */ 1435 Protect = MmProtectToValue[TempPte.u.Soft.Protection]; 1436 } 1437 else 1438 { 1439 /* The PTE is valid, so we can find the protection in the 1440 OriginalPte field of the PFN */ 1441 Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber); 1442 Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection]; 1443 } 1444 1445 /* Release the PFN database */ 1446 MiReleasePfnLock(OldIrql); 1447 } 1448 1449 /* Lock the working set again */ 1450 MiLockProcessWorkingSetForFault(CurrentProcess, 1451 CurrentThread, 1452 WsSafe, 1453 WsShared); 1454 1455 return Protect; 1456 } 1457 1458 /* In the easy case of transition or demand zero PTE just return its protection */ 1459 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection]; 1460 1461 /* If we get here, the PTE is valid, so look up the page in PFN database */ 1462 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber); 1463 if (!Pfn->u3.e1.PrototypePte) 1464 { 1465 /* Return protection of the original pte */ 1466 ASSERT(Pfn->u4.AweAllocation == 0); 1467 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection]; 1468 } 1469 1470 /* This is software PTE */ 1471 DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn); 1472 DPRINT("VA: %p\n", MiPteToAddress(&TempPte)); 1473 DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection); 1474 DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection); 1475 return MmProtectToValue[TempPte.u.Soft.Protection]; 1476 } 1477 1478 ULONG 1479 NTAPI 1480 MiQueryAddressState(IN PVOID Va, 1481 IN PMMVAD Vad, 1482 IN PEPROCESS TargetProcess, 1483 OUT PULONG ReturnedProtect, 1484 OUT PVOID *NextVa) 1485 { 1486 1487 PMMPTE PointerPte, ProtoPte; 1488 PMMPDE PointerPde; 1489 #if (_MI_PAGING_LEVELS >= 3) 1490 PMMPPE PointerPpe; 1491 #endif 1492 #if (_MI_PAGING_LEVELS >= 4) 1493 PMMPXE PointerPxe; 1494 #endif 1495 MMPTE TempPte, TempProtoPte; 1496 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE; 1497 ULONG State = MEM_RESERVE, Protect = 0; 1498 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) && 1499 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT))); 1500 1501 /* Only normal VADs supported */ 1502 ASSERT(Vad->u.VadFlags.VadType == VadNone); 1503 1504 /* Get the PDE and PTE for the address */ 1505 PointerPde = MiAddressToPde(Va); 1506 PointerPte = MiAddressToPte(Va); 1507 #if (_MI_PAGING_LEVELS >= 3) 1508 PointerPpe = MiAddressToPpe(Va); 1509 #endif 1510 #if (_MI_PAGING_LEVELS >= 4) 1511 PointerPxe = MiAddressToPxe(Va); 1512 #endif 1513 1514 /* Return the next range */ 1515 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE); 1516 1517 do 1518 { 1519 #if (_MI_PAGING_LEVELS >= 4) 1520 /* Does the PXE exist? */ 1521 if (PointerPxe->u.Long == 0) 1522 { 1523 /* It does not, next range starts at the next PXE */ 1524 *NextVa = MiPxeToAddress(PointerPxe + 1); 1525 break; 1526 } 1527 1528 /* Is the PXE valid? */ 1529 if (PointerPxe->u.Hard.Valid == 0) 1530 { 1531 /* Is isn't, fault it in (make the PPE accessible) */ 1532 MiMakeSystemAddressValid(PointerPpe, TargetProcess); 1533 } 1534 #endif 1535 #if (_MI_PAGING_LEVELS >= 3) 1536 /* Does the PPE exist? */ 1537 if (PointerPpe->u.Long == 0) 1538 { 1539 /* It does not, next range starts at the next PPE */ 1540 *NextVa = MiPpeToAddress(PointerPpe + 1); 1541 break; 1542 } 1543 1544 /* Is the PPE valid? */ 1545 if (PointerPpe->u.Hard.Valid == 0) 1546 { 1547 /* Is isn't, fault it in (make the PDE accessible) */ 1548 MiMakeSystemAddressValid(PointerPde, TargetProcess); 1549 } 1550 #endif 1551 1552 /* Does the PDE exist? */ 1553 if (PointerPde->u.Long == 0) 1554 { 1555 /* It does not, next range starts at the next PDE */ 1556 *NextVa = MiPdeToAddress(PointerPde + 1); 1557 break; 1558 } 1559 1560 /* Is the PDE valid? */ 1561 if (PointerPde->u.Hard.Valid == 0) 1562 { 1563 /* Is isn't, fault it in (make the PTE accessible) */ 1564 MiMakeSystemAddressValid(PointerPte, TargetProcess); 1565 } 1566 1567 /* We have a PTE that we can access now! */ 1568 ValidPte = TRUE; 1569 1570 } while (FALSE); 1571 1572 /* Is it safe to try reading the PTE? */ 1573 if (ValidPte) 1574 { 1575 /* FIXME: watch out for large pages */ 1576 ASSERT(PointerPde->u.Hard.LargePage == FALSE); 1577 1578 /* Capture the PTE */ 1579 TempPte = *PointerPte; 1580 if (TempPte.u.Long != 0) 1581 { 1582 /* The PTE is valid, so it's not zeroed out */ 1583 DemandZeroPte = FALSE; 1584 1585 /* Is it a decommited, invalid, or faulted PTE? */ 1586 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) && 1587 (TempPte.u.Hard.Valid == 0) && 1588 ((TempPte.u.Soft.Prototype == 0) || 1589 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 1590 { 1591 /* Otherwise our defaults should hold */ 1592 ASSERT(Protect == 0); 1593 ASSERT(State == MEM_RESERVE); 1594 } 1595 else 1596 { 1597 /* This means it's committed */ 1598 State = MEM_COMMIT; 1599 1600 /* We don't support these */ 1601 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory); 1602 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical); 1603 ASSERT(Vad->u.VadFlags.VadType != VadAwe); 1604 1605 /* Get protection state of this page */ 1606 Protect = MiGetPageProtection(PointerPte); 1607 1608 /* Check if this is an image-backed VAD */ 1609 if ((TempPte.u.Soft.Valid == 0) && 1610 (TempPte.u.Soft.Prototype == 1) && 1611 (Vad->u.VadFlags.PrivateMemory == 0) && 1612 (Vad->ControlArea)) 1613 { 1614 DPRINT1("Not supported\n"); 1615 ASSERT(FALSE); 1616 } 1617 } 1618 } 1619 } 1620 1621 /* Check if this was a demand-zero PTE, since we need to find the state */ 1622 if (DemandZeroPte) 1623 { 1624 /* Not yet handled */ 1625 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory); 1626 ASSERT(Vad->u.VadFlags.VadType != VadAwe); 1627 1628 /* Check if this is private commited memory, or an section-backed VAD */ 1629 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea)) 1630 { 1631 /* Tell caller about the next range */ 1632 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE); 1633 1634 /* Get the prototype PTE for this VAD */ 1635 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, 1636 (ULONG_PTR)Va >> PAGE_SHIFT); 1637 if (ProtoPte) 1638 { 1639 /* We should unlock the working set, but it's not being held! */ 1640 1641 /* Is the prototype PTE actually valid (committed)? */ 1642 TempProtoPte = *ProtoPte; 1643 if (TempProtoPte.u.Long) 1644 { 1645 /* Unless this is a memory-mapped file, handle it like private VAD */ 1646 State = MEM_COMMIT; 1647 ASSERT(Vad->u.VadFlags.VadType != VadImageMap); 1648 Protect = MmProtectToValue[Vad->u.VadFlags.Protection]; 1649 } 1650 1651 /* We should re-lock the working set */ 1652 } 1653 } 1654 else if (Vad->u.VadFlags.MemCommit) 1655 { 1656 /* This is committed memory */ 1657 State = MEM_COMMIT; 1658 1659 /* Convert the protection */ 1660 Protect = MmProtectToValue[Vad->u.VadFlags.Protection]; 1661 } 1662 } 1663 1664 /* Return the protection code */ 1665 *ReturnedProtect = Protect; 1666 return State; 1667 } 1668 1669 NTSTATUS 1670 NTAPI 1671 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle, 1672 IN PVOID BaseAddress, 1673 OUT PVOID MemoryInformation, 1674 IN SIZE_T MemoryInformationLength, 1675 OUT PSIZE_T ReturnLength) 1676 { 1677 PEPROCESS TargetProcess; 1678 NTSTATUS Status = STATUS_SUCCESS; 1679 PMMVAD Vad = NULL; 1680 PVOID Address, NextAddress; 1681 BOOLEAN Found = FALSE; 1682 ULONG NewProtect, NewState; 1683 ULONG_PTR BaseVpn; 1684 MEMORY_BASIC_INFORMATION MemoryInfo; 1685 KAPC_STATE ApcState; 1686 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 1687 PMEMORY_AREA MemoryArea; 1688 SIZE_T ResultLength; 1689 1690 /* Check for illegal addresses in user-space, or the shared memory area */ 1691 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) || 1692 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)) 1693 { 1694 Address = PAGE_ALIGN(BaseAddress); 1695 1696 /* Make up an info structure describing this range */ 1697 MemoryInfo.BaseAddress = Address; 1698 MemoryInfo.AllocationProtect = PAGE_READONLY; 1699 MemoryInfo.Type = MEM_PRIVATE; 1700 1701 /* Special case for shared data */ 1702 if (Address == (PVOID)MM_SHARED_USER_DATA_VA) 1703 { 1704 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA; 1705 MemoryInfo.State = MEM_COMMIT; 1706 MemoryInfo.Protect = PAGE_READONLY; 1707 MemoryInfo.RegionSize = PAGE_SIZE; 1708 } 1709 else 1710 { 1711 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1; 1712 MemoryInfo.State = MEM_RESERVE; 1713 MemoryInfo.Protect = PAGE_NOACCESS; 1714 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address; 1715 } 1716 1717 /* Return the data, NtQueryInformation already probed it*/ 1718 if (PreviousMode != KernelMode) 1719 { 1720 _SEH2_TRY 1721 { 1722 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1723 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1724 } 1725 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1726 { 1727 Status = _SEH2_GetExceptionCode(); 1728 } 1729 _SEH2_END; 1730 } 1731 else 1732 { 1733 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1734 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1735 } 1736 1737 return Status; 1738 } 1739 1740 /* Check if this is for a local or remote process */ 1741 if (ProcessHandle == NtCurrentProcess()) 1742 { 1743 TargetProcess = PsGetCurrentProcess(); 1744 } 1745 else 1746 { 1747 /* Reference the target process */ 1748 Status = ObReferenceObjectByHandle(ProcessHandle, 1749 PROCESS_QUERY_INFORMATION, 1750 PsProcessType, 1751 ExGetPreviousMode(), 1752 (PVOID*)&TargetProcess, 1753 NULL); 1754 if (!NT_SUCCESS(Status)) return Status; 1755 1756 /* Attach to it now */ 1757 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 1758 } 1759 1760 /* Lock the address space and make sure the process isn't already dead */ 1761 MmLockAddressSpace(&TargetProcess->Vm); 1762 if (TargetProcess->VmDeleted) 1763 { 1764 /* Unlock the address space of the process */ 1765 MmUnlockAddressSpace(&TargetProcess->Vm); 1766 1767 /* Check if we were attached */ 1768 if (ProcessHandle != NtCurrentProcess()) 1769 { 1770 /* Detach and dereference the process */ 1771 KeUnstackDetachProcess(&ApcState); 1772 ObDereferenceObject(TargetProcess); 1773 } 1774 1775 /* Bail out */ 1776 DPRINT1("Process is dying\n"); 1777 return STATUS_PROCESS_IS_TERMINATING; 1778 } 1779 1780 /* Loop the VADs */ 1781 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements); 1782 if (TargetProcess->VadRoot.NumberGenericTableElements) 1783 { 1784 /* Scan on the right */ 1785 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild; 1786 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT; 1787 while (Vad) 1788 { 1789 /* Check if this VAD covers the allocation range */ 1790 if ((BaseVpn >= Vad->StartingVpn) && 1791 (BaseVpn <= Vad->EndingVpn)) 1792 { 1793 /* We're done */ 1794 Found = TRUE; 1795 break; 1796 } 1797 1798 /* Check if this VAD is too high */ 1799 if (BaseVpn < Vad->StartingVpn) 1800 { 1801 /* Stop if there is no left child */ 1802 if (!Vad->LeftChild) break; 1803 1804 /* Search on the left next */ 1805 Vad = Vad->LeftChild; 1806 } 1807 else 1808 { 1809 /* Then this VAD is too low, keep searching on the right */ 1810 ASSERT(BaseVpn > Vad->EndingVpn); 1811 1812 /* Stop if there is no right child */ 1813 if (!Vad->RightChild) break; 1814 1815 /* Search on the right next */ 1816 Vad = Vad->RightChild; 1817 } 1818 } 1819 } 1820 1821 /* Was a VAD found? */ 1822 if (!Found) 1823 { 1824 Address = PAGE_ALIGN(BaseAddress); 1825 1826 /* Calculate region size */ 1827 if (Vad) 1828 { 1829 if (Vad->StartingVpn >= BaseVpn) 1830 { 1831 /* Region size is the free space till the start of that VAD */ 1832 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address; 1833 } 1834 else 1835 { 1836 /* Get the next VAD */ 1837 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad); 1838 if (Vad) 1839 { 1840 /* Region size is the free space till the start of that VAD */ 1841 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address; 1842 } 1843 else 1844 { 1845 /* Maximum possible region size with that base address */ 1846 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address; 1847 } 1848 } 1849 } 1850 else 1851 { 1852 /* Maximum possible region size with that base address */ 1853 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address; 1854 } 1855 1856 /* Unlock the address space of the process */ 1857 MmUnlockAddressSpace(&TargetProcess->Vm); 1858 1859 /* Check if we were attached */ 1860 if (ProcessHandle != NtCurrentProcess()) 1861 { 1862 /* Detach and derefernece the process */ 1863 KeUnstackDetachProcess(&ApcState); 1864 ObDereferenceObject(TargetProcess); 1865 } 1866 1867 /* Build the rest of the initial information block */ 1868 MemoryInfo.BaseAddress = Address; 1869 MemoryInfo.AllocationBase = NULL; 1870 MemoryInfo.AllocationProtect = 0; 1871 MemoryInfo.State = MEM_FREE; 1872 MemoryInfo.Protect = PAGE_NOACCESS; 1873 MemoryInfo.Type = 0; 1874 1875 /* Return the data, NtQueryInformation already probed it*/ 1876 if (PreviousMode != KernelMode) 1877 { 1878 _SEH2_TRY 1879 { 1880 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1881 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1882 } 1883 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1884 { 1885 Status = _SEH2_GetExceptionCode(); 1886 } 1887 _SEH2_END; 1888 } 1889 else 1890 { 1891 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1892 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1893 } 1894 1895 return Status; 1896 } 1897 1898 /* Set the correct memory type based on what kind of VAD this is */ 1899 if ((Vad->u.VadFlags.PrivateMemory) || 1900 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 1901 { 1902 MemoryInfo.Type = MEM_PRIVATE; 1903 } 1904 else if (Vad->u.VadFlags.VadType == VadImageMap) 1905 { 1906 MemoryInfo.Type = MEM_IMAGE; 1907 } 1908 else 1909 { 1910 MemoryInfo.Type = MEM_MAPPED; 1911 } 1912 1913 /* Find the memory area the specified address belongs to */ 1914 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress); 1915 ASSERT(MemoryArea != NULL); 1916 1917 /* Determine information dependent on the memory area type */ 1918 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 1919 { 1920 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength); 1921 if (!NT_SUCCESS(Status)) 1922 { 1923 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n", 1924 MemoryArea, MA_GetStartingAddress(MemoryArea), MA_GetEndingAddress(MemoryArea), BaseAddress); 1925 ASSERT(NT_SUCCESS(Status)); 1926 } 1927 } 1928 else 1929 { 1930 /* Build the initial information block */ 1931 Address = PAGE_ALIGN(BaseAddress); 1932 MemoryInfo.BaseAddress = Address; 1933 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); 1934 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; 1935 MemoryInfo.Type = MEM_PRIVATE; 1936 1937 /* Acquire the working set lock (shared is enough) */ 1938 MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread()); 1939 1940 /* Find the largest chunk of memory which has the same state and protection mask */ 1941 MemoryInfo.State = MiQueryAddressState(Address, 1942 Vad, 1943 TargetProcess, 1944 &MemoryInfo.Protect, 1945 &NextAddress); 1946 Address = NextAddress; 1947 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn) 1948 { 1949 /* Keep going unless the state or protection mask changed */ 1950 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress); 1951 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break; 1952 Address = NextAddress; 1953 } 1954 1955 /* Release the working set lock */ 1956 MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread()); 1957 1958 /* Check if we went outside of the VAD */ 1959 if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn) 1960 { 1961 /* Set the end of the VAD as the end address */ 1962 Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT); 1963 } 1964 1965 /* Now that we know the last VA address, calculate the region size */ 1966 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress); 1967 } 1968 1969 /* Unlock the address space of the process */ 1970 MmUnlockAddressSpace(&TargetProcess->Vm); 1971 1972 /* Check if we were attached */ 1973 if (ProcessHandle != NtCurrentProcess()) 1974 { 1975 /* Detach and derefernece the process */ 1976 KeUnstackDetachProcess(&ApcState); 1977 ObDereferenceObject(TargetProcess); 1978 } 1979 1980 /* Return the data, NtQueryInformation already probed it */ 1981 if (PreviousMode != KernelMode) 1982 { 1983 _SEH2_TRY 1984 { 1985 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1986 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1987 } 1988 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1989 { 1990 Status = _SEH2_GetExceptionCode(); 1991 } 1992 _SEH2_END; 1993 } 1994 else 1995 { 1996 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 1997 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 1998 } 1999 2000 /* All went well */ 2001 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx " 2002 "State: %lx Type: %lx Size: %lx\n", 2003 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase, 2004 MemoryInfo.AllocationProtect, MemoryInfo.Protect, 2005 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize); 2006 2007 return Status; 2008 } 2009 2010 BOOLEAN 2011 NTAPI 2012 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress, 2013 IN ULONG_PTR EndingAddress, 2014 IN PMMVAD Vad, 2015 IN PEPROCESS Process) 2016 { 2017 PMMPTE PointerPte, LastPte; 2018 PMMPDE PointerPde; 2019 BOOLEAN OnBoundary = TRUE; 2020 PAGED_CODE(); 2021 2022 /* Get the PDE and PTE addresses */ 2023 PointerPde = MiAddressToPde(StartingAddress); 2024 PointerPte = MiAddressToPte(StartingAddress); 2025 LastPte = MiAddressToPte(EndingAddress); 2026 2027 /* Loop all the PTEs */ 2028 while (PointerPte <= LastPte) 2029 { 2030 /* Check if we've hit an new PDE boundary */ 2031 if (OnBoundary) 2032 { 2033 /* Is this PDE demand zero? */ 2034 PointerPde = MiPteToPde(PointerPte); 2035 if (PointerPde->u.Long != 0) 2036 { 2037 /* It isn't -- is it valid? */ 2038 if (PointerPde->u.Hard.Valid == 0) 2039 { 2040 /* Nope, fault it in */ 2041 MiMakeSystemAddressValid(PointerPte, Process); 2042 } 2043 } 2044 else 2045 { 2046 /* The PTE was already valid, so move to the next one */ 2047 PointerPde++; 2048 PointerPte = MiPdeToPte(PointerPde); 2049 2050 /* Is the entire VAD committed? If not, fail */ 2051 if (!Vad->u.VadFlags.MemCommit) return FALSE; 2052 2053 /* New loop iteration with our new, on-boundary PTE. */ 2054 continue; 2055 } 2056 } 2057 2058 /* Is the PTE demand zero? */ 2059 if (PointerPte->u.Long == 0) 2060 { 2061 /* Is the entire VAD committed? If not, fail */ 2062 if (!Vad->u.VadFlags.MemCommit) return FALSE; 2063 } 2064 else 2065 { 2066 /* It isn't -- is it a decommited, invalid, or faulted PTE? */ 2067 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) && 2068 (PointerPte->u.Hard.Valid == 0) && 2069 ((PointerPte->u.Soft.Prototype == 0) || 2070 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 2071 { 2072 /* Then part of the range is decommitted, so fail */ 2073 return FALSE; 2074 } 2075 } 2076 2077 /* Move to the next PTE */ 2078 PointerPte++; 2079 OnBoundary = MiIsPteOnPdeBoundary(PointerPte); 2080 } 2081 2082 /* All PTEs seem valid, and no VAD checks failed, the range is okay */ 2083 return TRUE; 2084 } 2085 2086 NTSTATUS 2087 NTAPI 2088 MiRosProtectVirtualMemory(IN PEPROCESS Process, 2089 IN OUT PVOID *BaseAddress, 2090 IN OUT PSIZE_T NumberOfBytesToProtect, 2091 IN ULONG NewAccessProtection, 2092 OUT PULONG OldAccessProtection OPTIONAL) 2093 { 2094 PMEMORY_AREA MemoryArea; 2095 PMMSUPPORT AddressSpace; 2096 ULONG OldAccessProtection_; 2097 NTSTATUS Status; 2098 2099 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress); 2100 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress); 2101 2102 AddressSpace = &Process->Vm; 2103 MmLockAddressSpace(AddressSpace); 2104 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress); 2105 if (MemoryArea == NULL || MemoryArea->DeleteInProgress) 2106 { 2107 MmUnlockAddressSpace(AddressSpace); 2108 return STATUS_UNSUCCESSFUL; 2109 } 2110 2111 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_; 2112 2113 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW); 2114 Status = MmProtectSectionView(AddressSpace, 2115 MemoryArea, 2116 *BaseAddress, 2117 *NumberOfBytesToProtect, 2118 NewAccessProtection, 2119 OldAccessProtection); 2120 2121 MmUnlockAddressSpace(AddressSpace); 2122 2123 return Status; 2124 } 2125 2126 NTSTATUS 2127 NTAPI 2128 MiProtectVirtualMemory(IN PEPROCESS Process, 2129 IN OUT PVOID *BaseAddress, 2130 IN OUT PSIZE_T NumberOfBytesToProtect, 2131 IN ULONG NewAccessProtection, 2132 OUT PULONG OldAccessProtection OPTIONAL) 2133 { 2134 PMEMORY_AREA MemoryArea; 2135 PMMVAD Vad; 2136 PMMSUPPORT AddressSpace; 2137 ULONG_PTR StartingAddress, EndingAddress; 2138 PMMPTE PointerPte, LastPte; 2139 PMMPDE PointerPde; 2140 MMPTE PteContents; 2141 PMMPFN Pfn1; 2142 ULONG ProtectionMask, OldProtect; 2143 BOOLEAN Committed; 2144 NTSTATUS Status = STATUS_SUCCESS; 2145 PETHREAD Thread = PsGetCurrentThread(); 2146 TABLE_SEARCH_RESULT Result; 2147 2148 /* Calculate base address for the VAD */ 2149 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress)); 2150 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1)); 2151 2152 /* Calculate the protection mask and make sure it's valid */ 2153 ProtectionMask = MiMakeProtectionMask(NewAccessProtection); 2154 if (ProtectionMask == MM_INVALID_PROTECTION) 2155 { 2156 DPRINT1("Invalid protection mask\n"); 2157 return STATUS_INVALID_PAGE_PROTECTION; 2158 } 2159 2160 /* Check for ROS specific memory area */ 2161 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress); 2162 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)) 2163 { 2164 /* Evil hack */ 2165 return MiRosProtectVirtualMemory(Process, 2166 BaseAddress, 2167 NumberOfBytesToProtect, 2168 NewAccessProtection, 2169 OldAccessProtection); 2170 } 2171 2172 /* Lock the address space and make sure the process isn't already dead */ 2173 AddressSpace = MmGetCurrentAddressSpace(); 2174 MmLockAddressSpace(AddressSpace); 2175 if (Process->VmDeleted) 2176 { 2177 DPRINT1("Process is dying\n"); 2178 Status = STATUS_PROCESS_IS_TERMINATING; 2179 goto FailPath; 2180 } 2181 2182 /* Get the VAD for this address range, and make sure it exists */ 2183 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 2184 EndingAddress >> PAGE_SHIFT, 2185 &Process->VadRoot, 2186 (PMMADDRESS_NODE*)&Vad); 2187 if (Result != TableFoundNode) 2188 { 2189 DPRINT("Could not find a VAD for this allocation\n"); 2190 Status = STATUS_CONFLICTING_ADDRESSES; 2191 goto FailPath; 2192 } 2193 2194 /* Make sure the address is within this VAD's boundaries */ 2195 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) || 2196 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn)) 2197 { 2198 Status = STATUS_CONFLICTING_ADDRESSES; 2199 goto FailPath; 2200 } 2201 2202 /* These kinds of VADs are not supported atm */ 2203 if ((Vad->u.VadFlags.VadType == VadAwe) || 2204 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 2205 (Vad->u.VadFlags.VadType == VadLargePages)) 2206 { 2207 DPRINT1("Illegal VAD for attempting to set protection\n"); 2208 Status = STATUS_CONFLICTING_ADDRESSES; 2209 goto FailPath; 2210 } 2211 2212 /* Check for a VAD whose protection can't be changed */ 2213 if (Vad->u.VadFlags.NoChange == 1) 2214 { 2215 DPRINT1("Trying to change protection of a NoChange VAD\n"); 2216 Status = STATUS_INVALID_PAGE_PROTECTION; 2217 goto FailPath; 2218 } 2219 2220 /* Is this section, or private memory? */ 2221 if (Vad->u.VadFlags.PrivateMemory == 0) 2222 { 2223 /* Not yet supported */ 2224 if (Vad->u.VadFlags.VadType == VadLargePageSection) 2225 { 2226 DPRINT1("Illegal VAD for attempting to set protection\n"); 2227 Status = STATUS_CONFLICTING_ADDRESSES; 2228 goto FailPath; 2229 } 2230 2231 /* Rotate VADs are not yet supported */ 2232 if (Vad->u.VadFlags.VadType == VadRotatePhysical) 2233 { 2234 DPRINT1("Illegal VAD for attempting to set protection\n"); 2235 Status = STATUS_CONFLICTING_ADDRESSES; 2236 goto FailPath; 2237 } 2238 2239 /* Not valid on section files */ 2240 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) 2241 { 2242 /* Fail */ 2243 DPRINT1("Invalid protection flags for section\n"); 2244 Status = STATUS_INVALID_PARAMETER_4; 2245 goto FailPath; 2246 } 2247 2248 /* Check if data or page file mapping protection PTE is compatible */ 2249 if (!Vad->ControlArea->u.Flags.Image) 2250 { 2251 /* Not yet */ 2252 DPRINT1("Fixme: Not checking for valid protection\n"); 2253 } 2254 2255 /* This is a section, and this is not yet supported */ 2256 DPRINT1("Section protection not yet supported\n"); 2257 OldProtect = 0; 2258 } 2259 else 2260 { 2261 /* Private memory, check protection flags */ 2262 if ((NewAccessProtection & PAGE_WRITECOPY) || 2263 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY)) 2264 { 2265 DPRINT1("Invalid protection flags for private memory\n"); 2266 Status = STATUS_INVALID_PARAMETER_4; 2267 goto FailPath; 2268 } 2269 2270 /* Lock the working set */ 2271 MiLockProcessWorkingSetUnsafe(Process, Thread); 2272 2273 /* Check if all pages in this range are committed */ 2274 Committed = MiIsEntireRangeCommitted(StartingAddress, 2275 EndingAddress, 2276 Vad, 2277 Process); 2278 if (!Committed) 2279 { 2280 /* Fail */ 2281 DPRINT1("The entire range is not committed\n"); 2282 Status = STATUS_NOT_COMMITTED; 2283 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2284 goto FailPath; 2285 } 2286 2287 /* Compute starting and ending PTE and PDE addresses */ 2288 PointerPde = MiAddressToPde(StartingAddress); 2289 PointerPte = MiAddressToPte(StartingAddress); 2290 LastPte = MiAddressToPte(EndingAddress); 2291 2292 /* Make this PDE valid */ 2293 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2294 2295 /* Save protection of the first page */ 2296 if (PointerPte->u.Long != 0) 2297 { 2298 /* Capture the page protection and make the PDE valid */ 2299 OldProtect = MiGetPageProtection(PointerPte); 2300 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2301 } 2302 else 2303 { 2304 /* Grab the old protection from the VAD itself */ 2305 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; 2306 } 2307 2308 /* Loop all the PTEs now */ 2309 while (PointerPte <= LastPte) 2310 { 2311 /* Check if we've crossed a PDE boundary and make the new PDE valid too */ 2312 if (MiIsPteOnPdeBoundary(PointerPte)) 2313 { 2314 PointerPde = MiPteToPde(PointerPte); 2315 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2316 } 2317 2318 /* Capture the PTE and check if it was empty */ 2319 PteContents = *PointerPte; 2320 if (PteContents.u.Long == 0) 2321 { 2322 /* This used to be a zero PTE and it no longer is, so we must add a 2323 reference to the pagetable. */ 2324 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 2325 } 2326 2327 /* Check what kind of PTE we are dealing with */ 2328 if (PteContents.u.Hard.Valid == 1) 2329 { 2330 /* Get the PFN entry */ 2331 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents)); 2332 2333 /* We don't support this yet */ 2334 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 2335 2336 /* Check if the page should not be accessible at all */ 2337 if ((NewAccessProtection & PAGE_NOACCESS) || 2338 (NewAccessProtection & PAGE_GUARD)) 2339 { 2340 KIRQL OldIrql = MiAcquirePfnLock(); 2341 2342 /* Mark the PTE as transition and change its protection */ 2343 PteContents.u.Hard.Valid = 0; 2344 PteContents.u.Soft.Transition = 1; 2345 PteContents.u.Trans.Protection = ProtectionMask; 2346 /* Decrease PFN share count and write the PTE */ 2347 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents)); 2348 // FIXME: remove the page from the WS 2349 MI_WRITE_INVALID_PTE(PointerPte, PteContents); 2350 #ifdef CONFIG_SMP 2351 // FIXME: Should invalidate entry in every CPU TLB 2352 ASSERT(FALSE); 2353 #endif 2354 KeInvalidateTlbEntry(MiPteToAddress(PointerPte)); 2355 2356 /* We are done for this PTE */ 2357 MiReleasePfnLock(OldIrql); 2358 } 2359 else 2360 { 2361 /* Write the protection mask and write it with a TLB flush */ 2362 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask; 2363 MiFlushTbAndCapture(Vad, 2364 PointerPte, 2365 ProtectionMask, 2366 Pfn1, 2367 TRUE); 2368 } 2369 } 2370 else 2371 { 2372 /* We don't support these cases yet */ 2373 ASSERT(PteContents.u.Soft.Prototype == 0); 2374 //ASSERT(PteContents.u.Soft.Transition == 0); 2375 2376 /* The PTE is already demand-zero, just update the protection mask */ 2377 PteContents.u.Soft.Protection = ProtectionMask; 2378 MI_WRITE_INVALID_PTE(PointerPte, PteContents); 2379 ASSERT(PointerPte->u.Long != 0); 2380 } 2381 2382 /* Move to the next PTE */ 2383 PointerPte++; 2384 } 2385 2386 /* Unlock the working set */ 2387 MiUnlockProcessWorkingSetUnsafe(Process, Thread); 2388 } 2389 2390 /* Unlock the address space */ 2391 MmUnlockAddressSpace(AddressSpace); 2392 2393 /* Return parameters and success */ 2394 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1; 2395 *BaseAddress = (PVOID)StartingAddress; 2396 *OldAccessProtection = OldProtect; 2397 return STATUS_SUCCESS; 2398 2399 FailPath: 2400 /* Unlock the address space and return the failure code */ 2401 MmUnlockAddressSpace(AddressSpace); 2402 return Status; 2403 } 2404 2405 VOID 2406 NTAPI 2407 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde, 2408 IN PEPROCESS TargetProcess, 2409 IN KIRQL OldIrql) 2410 { 2411 PMMPTE PointerPte, PointerPpe, PointerPxe; 2412 2413 // 2414 // Sanity checks. The latter is because we only use this function with the 2415 // PFN lock not held, so it may go away in the future. 2416 // 2417 ASSERT(KeAreAllApcsDisabled() == TRUE); 2418 ASSERT(OldIrql == MM_NOIRQL); 2419 2420 // 2421 // Also get the PPE and PXE. This is okay not to #ifdef because they will 2422 // return the same address as the PDE on 2-level page table systems. 2423 // 2424 // If everything is already valid, there is nothing to do. 2425 // 2426 PointerPpe = MiAddressToPte(PointerPde); 2427 PointerPxe = MiAddressToPde(PointerPde); 2428 if ((PointerPxe->u.Hard.Valid) && 2429 (PointerPpe->u.Hard.Valid) && 2430 (PointerPde->u.Hard.Valid)) 2431 { 2432 return; 2433 } 2434 2435 // 2436 // At least something is invalid, so begin by getting the PTE for the PDE itself 2437 // and then lookup each additional level. We must do it in this precise order 2438 // because the pagfault.c code (as well as in Windows) depends that the next 2439 // level up (higher) must be valid when faulting a lower level 2440 // 2441 PointerPte = MiPteToAddress(PointerPde); 2442 do 2443 { 2444 // 2445 // Make sure APCs continued to be disabled 2446 // 2447 ASSERT(KeAreAllApcsDisabled() == TRUE); 2448 2449 // 2450 // First, make the PXE valid if needed 2451 // 2452 if (!PointerPxe->u.Hard.Valid) 2453 { 2454 MiMakeSystemAddressValid(PointerPpe, TargetProcess); 2455 ASSERT(PointerPxe->u.Hard.Valid == 1); 2456 } 2457 2458 // 2459 // Next, the PPE 2460 // 2461 if (!PointerPpe->u.Hard.Valid) 2462 { 2463 MiMakeSystemAddressValid(PointerPde, TargetProcess); 2464 ASSERT(PointerPpe->u.Hard.Valid == 1); 2465 } 2466 2467 // 2468 // And finally, make the PDE itself valid. 2469 // 2470 MiMakeSystemAddressValid(PointerPte, TargetProcess); 2471 2472 // 2473 // This should've worked the first time so the loop is really just for 2474 // show -- ASSERT that we're actually NOT going to be looping. 2475 // 2476 ASSERT(PointerPxe->u.Hard.Valid == 1); 2477 ASSERT(PointerPpe->u.Hard.Valid == 1); 2478 ASSERT(PointerPde->u.Hard.Valid == 1); 2479 } while (!(PointerPxe->u.Hard.Valid) || 2480 !(PointerPpe->u.Hard.Valid) || 2481 !(PointerPde->u.Hard.Valid)); 2482 } 2483 2484 VOID 2485 NTAPI 2486 MiProcessValidPteList(IN PMMPTE *ValidPteList, 2487 IN ULONG Count) 2488 { 2489 KIRQL OldIrql; 2490 ULONG i; 2491 MMPTE TempPte; 2492 PFN_NUMBER PageFrameIndex; 2493 PMMPFN Pfn1, Pfn2; 2494 2495 // 2496 // Acquire the PFN lock and loop all the PTEs in the list 2497 // 2498 OldIrql = MiAcquirePfnLock(); 2499 for (i = 0; i != Count; i++) 2500 { 2501 // 2502 // The PTE must currently be valid 2503 // 2504 TempPte = *ValidPteList[i]; 2505 ASSERT(TempPte.u.Hard.Valid == 1); 2506 2507 // 2508 // Get the PFN entry for the page itself, and then for its page table 2509 // 2510 PageFrameIndex = PFN_FROM_PTE(&TempPte); 2511 Pfn1 = MiGetPfnEntry(PageFrameIndex); 2512 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame); 2513 2514 // 2515 // Decrement the share count on the page table, and then on the page 2516 // itself 2517 // 2518 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame); 2519 MI_SET_PFN_DELETED(Pfn1); 2520 MiDecrementShareCount(Pfn1, PageFrameIndex); 2521 2522 // 2523 // Make the page decommitted 2524 // 2525 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte); 2526 } 2527 2528 // 2529 // All the PTEs have been dereferenced and made invalid, flush the TLB now 2530 // and then release the PFN lock 2531 // 2532 KeFlushCurrentTb(); 2533 MiReleasePfnLock(OldIrql); 2534 } 2535 2536 ULONG 2537 NTAPI 2538 MiDecommitPages(IN PVOID StartingAddress, 2539 IN PMMPTE EndingPte, 2540 IN PEPROCESS Process, 2541 IN PMMVAD Vad) 2542 { 2543 PMMPTE PointerPte, CommitPte = NULL; 2544 PMMPDE PointerPde; 2545 ULONG CommitReduction = 0; 2546 PMMPTE ValidPteList[256]; 2547 ULONG PteCount = 0; 2548 PMMPFN Pfn1; 2549 MMPTE PteContents; 2550 PETHREAD CurrentThread = PsGetCurrentThread(); 2551 2552 // 2553 // Get the PTE and PTE for the address, and lock the working set 2554 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the 2555 // commited range ends so that we can do the right accounting. 2556 // 2557 PointerPde = MiAddressToPde(StartingAddress); 2558 PointerPte = MiAddressToPte(StartingAddress); 2559 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT); 2560 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 2561 2562 // 2563 // Make the PDE valid, and now loop through each page's worth of data 2564 // 2565 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2566 while (PointerPte <= EndingPte) 2567 { 2568 // 2569 // Check if we've crossed a PDE boundary 2570 // 2571 if (MiIsPteOnPdeBoundary(PointerPte)) 2572 { 2573 // 2574 // Get the new PDE and flush the valid PTEs we had built up until 2575 // now. This helps reduce the amount of TLB flushing we have to do. 2576 // Note that Windows does a much better job using timestamps and 2577 // such, and does not flush the entire TLB all the time, but right 2578 // now we have bigger problems to worry about than TLB flushing. 2579 // 2580 PointerPde = MiAddressToPde(StartingAddress); 2581 if (PteCount) 2582 { 2583 MiProcessValidPteList(ValidPteList, PteCount); 2584 PteCount = 0; 2585 } 2586 2587 // 2588 // Make this PDE valid 2589 // 2590 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 2591 } 2592 2593 // 2594 // Read this PTE. It might be active or still demand-zero. 2595 // 2596 PteContents = *PointerPte; 2597 if (PteContents.u.Long) 2598 { 2599 // 2600 // The PTE is active. It might be valid and in a working set, or 2601 // it might be a prototype PTE or paged out or even in transition. 2602 // 2603 if (PointerPte->u.Long == MmDecommittedPte.u.Long) 2604 { 2605 // 2606 // It's already decommited, so there's nothing for us to do here 2607 // 2608 CommitReduction++; 2609 } 2610 else 2611 { 2612 // 2613 // Remove it from the counters, and check if it was valid or not 2614 // 2615 //Process->NumberOfPrivatePages--; 2616 if (PteContents.u.Hard.Valid) 2617 { 2618 // 2619 // It's valid. At this point make sure that it is not a ROS 2620 // PFN. Also, we don't support ProtoPTEs in this code path. 2621 // 2622 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber); 2623 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE); 2624 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE); 2625 2626 // 2627 // Flush any pending PTEs that we had not yet flushed, if our 2628 // list has gotten too big, then add this PTE to the flush list. 2629 // 2630 if (PteCount == 256) 2631 { 2632 MiProcessValidPteList(ValidPteList, PteCount); 2633 PteCount = 0; 2634 } 2635 ValidPteList[PteCount++] = PointerPte; 2636 } 2637 else 2638 { 2639 // 2640 // We do not support any of these other scenarios at the moment 2641 // 2642 ASSERT(PteContents.u.Soft.Prototype == 0); 2643 ASSERT(PteContents.u.Soft.Transition == 0); 2644 ASSERT(PteContents.u.Soft.PageFileHigh == 0); 2645 2646 // 2647 // So the only other possibility is that it is still a demand 2648 // zero PTE, in which case we undo the accounting we did 2649 // earlier and simply make the page decommitted. 2650 // 2651 //Process->NumberOfPrivatePages++; 2652 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 2653 } 2654 } 2655 } 2656 else 2657 { 2658 // 2659 // This used to be a zero PTE and it no longer is, so we must add a 2660 // reference to the pagetable. 2661 // 2662 MiIncrementPageTableReferences(StartingAddress); 2663 2664 // 2665 // Next, we account for decommitted PTEs and make the PTE as such 2666 // 2667 if (PointerPte > CommitPte) CommitReduction++; 2668 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 2669 } 2670 2671 // 2672 // Move to the next PTE and the next address 2673 // 2674 PointerPte++; 2675 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE); 2676 } 2677 2678 // 2679 // Flush any dangling PTEs from the loop in the last page table, and then 2680 // release the working set and return the commit reduction accounting. 2681 // 2682 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount); 2683 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 2684 return CommitReduction; 2685 } 2686 2687 /* PUBLIC FUNCTIONS ***********************************************************/ 2688 2689 /* 2690 * @unimplemented 2691 */ 2692 PVOID 2693 NTAPI 2694 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress) 2695 { 2696 UNIMPLEMENTED; 2697 return 0; 2698 } 2699 2700 /* 2701 * @unimplemented 2702 */ 2703 PVOID 2704 NTAPI 2705 MmSecureVirtualMemory(IN PVOID Address, 2706 IN SIZE_T Length, 2707 IN ULONG Mode) 2708 { 2709 static ULONG Warn; if (!Warn++) UNIMPLEMENTED; 2710 return Address; 2711 } 2712 2713 /* 2714 * @unimplemented 2715 */ 2716 VOID 2717 NTAPI 2718 MmUnsecureVirtualMemory(IN PVOID SecureMem) 2719 { 2720 static ULONG Warn; if (!Warn++) UNIMPLEMENTED; 2721 } 2722 2723 /* SYSTEM CALLS ***************************************************************/ 2724 2725 NTSTATUS 2726 NTAPI 2727 NtReadVirtualMemory(IN HANDLE ProcessHandle, 2728 IN PVOID BaseAddress, 2729 OUT PVOID Buffer, 2730 IN SIZE_T NumberOfBytesToRead, 2731 OUT PSIZE_T NumberOfBytesRead OPTIONAL) 2732 { 2733 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2734 PEPROCESS Process; 2735 NTSTATUS Status = STATUS_SUCCESS; 2736 SIZE_T BytesRead = 0; 2737 PAGED_CODE(); 2738 2739 // 2740 // Check if we came from user mode 2741 // 2742 if (PreviousMode != KernelMode) 2743 { 2744 // 2745 // Validate the read addresses 2746 // 2747 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) || 2748 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) || 2749 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) || 2750 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress)) 2751 { 2752 // 2753 // Don't allow to write into kernel space 2754 // 2755 return STATUS_ACCESS_VIOLATION; 2756 } 2757 2758 // 2759 // Enter SEH for probe 2760 // 2761 _SEH2_TRY 2762 { 2763 // 2764 // Probe the output value 2765 // 2766 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead); 2767 } 2768 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2769 { 2770 // 2771 // Get exception code 2772 // 2773 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2774 } 2775 _SEH2_END; 2776 } 2777 2778 // 2779 // Don't do zero-byte transfers 2780 // 2781 if (NumberOfBytesToRead) 2782 { 2783 // 2784 // Reference the process 2785 // 2786 Status = ObReferenceObjectByHandle(ProcessHandle, 2787 PROCESS_VM_READ, 2788 PsProcessType, 2789 PreviousMode, 2790 (PVOID*)(&Process), 2791 NULL); 2792 if (NT_SUCCESS(Status)) 2793 { 2794 // 2795 // Do the copy 2796 // 2797 Status = MmCopyVirtualMemory(Process, 2798 BaseAddress, 2799 PsGetCurrentProcess(), 2800 Buffer, 2801 NumberOfBytesToRead, 2802 PreviousMode, 2803 &BytesRead); 2804 2805 // 2806 // Dereference the process 2807 // 2808 ObDereferenceObject(Process); 2809 } 2810 } 2811 2812 // 2813 // Check if the caller sent this parameter 2814 // 2815 if (NumberOfBytesRead) 2816 { 2817 // 2818 // Enter SEH to guard write 2819 // 2820 _SEH2_TRY 2821 { 2822 // 2823 // Return the number of bytes read 2824 // 2825 *NumberOfBytesRead = BytesRead; 2826 } 2827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2828 { 2829 } 2830 _SEH2_END; 2831 } 2832 2833 // 2834 // Return status 2835 // 2836 return Status; 2837 } 2838 2839 NTSTATUS 2840 NTAPI 2841 NtWriteVirtualMemory(IN HANDLE ProcessHandle, 2842 IN PVOID BaseAddress, 2843 IN PVOID Buffer, 2844 IN SIZE_T NumberOfBytesToWrite, 2845 OUT PSIZE_T NumberOfBytesWritten OPTIONAL) 2846 { 2847 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 2848 PEPROCESS Process; 2849 NTSTATUS Status = STATUS_SUCCESS; 2850 SIZE_T BytesWritten = 0; 2851 PAGED_CODE(); 2852 2853 // 2854 // Check if we came from user mode 2855 // 2856 if (PreviousMode != KernelMode) 2857 { 2858 // 2859 // Validate the read addresses 2860 // 2861 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) || 2862 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) || 2863 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) || 2864 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress)) 2865 { 2866 // 2867 // Don't allow to write into kernel space 2868 // 2869 return STATUS_ACCESS_VIOLATION; 2870 } 2871 2872 // 2873 // Enter SEH for probe 2874 // 2875 _SEH2_TRY 2876 { 2877 // 2878 // Probe the output value 2879 // 2880 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten); 2881 } 2882 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2883 { 2884 // 2885 // Get exception code 2886 // 2887 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 2888 } 2889 _SEH2_END; 2890 } 2891 2892 // 2893 // Don't do zero-byte transfers 2894 // 2895 if (NumberOfBytesToWrite) 2896 { 2897 // 2898 // Reference the process 2899 // 2900 Status = ObReferenceObjectByHandle(ProcessHandle, 2901 PROCESS_VM_WRITE, 2902 PsProcessType, 2903 PreviousMode, 2904 (PVOID*)&Process, 2905 NULL); 2906 if (NT_SUCCESS(Status)) 2907 { 2908 // 2909 // Do the copy 2910 // 2911 Status = MmCopyVirtualMemory(PsGetCurrentProcess(), 2912 Buffer, 2913 Process, 2914 BaseAddress, 2915 NumberOfBytesToWrite, 2916 PreviousMode, 2917 &BytesWritten); 2918 2919 // 2920 // Dereference the process 2921 // 2922 ObDereferenceObject(Process); 2923 } 2924 } 2925 2926 // 2927 // Check if the caller sent this parameter 2928 // 2929 if (NumberOfBytesWritten) 2930 { 2931 // 2932 // Enter SEH to guard write 2933 // 2934 _SEH2_TRY 2935 { 2936 // 2937 // Return the number of bytes written 2938 // 2939 *NumberOfBytesWritten = BytesWritten; 2940 } 2941 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2942 { 2943 } 2944 _SEH2_END; 2945 } 2946 2947 // 2948 // Return status 2949 // 2950 return Status; 2951 } 2952 2953 NTSTATUS 2954 NTAPI 2955 NtFlushInstructionCache(_In_ HANDLE ProcessHandle, 2956 _In_opt_ PVOID BaseAddress, 2957 _In_ SIZE_T FlushSize) 2958 { 2959 KAPC_STATE ApcState; 2960 PKPROCESS Process; 2961 NTSTATUS Status; 2962 PAGED_CODE(); 2963 2964 /* Is a base address given? */ 2965 if (BaseAddress != NULL) 2966 { 2967 /* If the requested size is 0, there is nothing to do */ 2968 if (FlushSize == 0) 2969 { 2970 return STATUS_SUCCESS; 2971 } 2972 2973 /* Is this a user mode call? */ 2974 if (ExGetPreviousMode() != KernelMode) 2975 { 2976 /* Make sure the base address is in user space */ 2977 if (BaseAddress > MmHighestUserAddress) 2978 { 2979 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress); 2980 return STATUS_ACCESS_VIOLATION; 2981 } 2982 } 2983 } 2984 2985 /* Is another process requested? */ 2986 if (ProcessHandle != NtCurrentProcess()) 2987 { 2988 /* Reference the process */ 2989 Status = ObReferenceObjectByHandle(ProcessHandle, 2990 PROCESS_VM_WRITE, 2991 PsProcessType, 2992 ExGetPreviousMode(), 2993 (PVOID*)&Process, 2994 NULL); 2995 if (!NT_SUCCESS(Status)) 2996 { 2997 DPRINT1("Failed to reference the process %p\n", ProcessHandle); 2998 return Status; 2999 } 3000 3001 /* Attach to the process */ 3002 KeStackAttachProcess(Process, &ApcState); 3003 } 3004 3005 /* Forward to Ke */ 3006 KeSweepICache(BaseAddress, FlushSize); 3007 3008 /* Check if we attached */ 3009 if (ProcessHandle != NtCurrentProcess()) 3010 { 3011 /* Detach from the process and dereference it */ 3012 KeUnstackDetachProcess(&ApcState); 3013 ObDereferenceObject(Process); 3014 } 3015 3016 /* All done, return to caller */ 3017 return STATUS_SUCCESS; 3018 } 3019 3020 NTSTATUS 3021 NTAPI 3022 NtProtectVirtualMemory(IN HANDLE ProcessHandle, 3023 IN OUT PVOID *UnsafeBaseAddress, 3024 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect, 3025 IN ULONG NewAccessProtection, 3026 OUT PULONG UnsafeOldAccessProtection) 3027 { 3028 PEPROCESS Process; 3029 ULONG OldAccessProtection; 3030 ULONG Protection; 3031 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3032 PVOID BaseAddress = NULL; 3033 SIZE_T NumberOfBytesToProtect = 0; 3034 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3035 NTSTATUS Status; 3036 BOOLEAN Attached = FALSE; 3037 KAPC_STATE ApcState; 3038 PAGED_CODE(); 3039 3040 // 3041 // Check for valid protection flags 3042 // 3043 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE); 3044 if (Protection != PAGE_NOACCESS && 3045 Protection != PAGE_READONLY && 3046 Protection != PAGE_READWRITE && 3047 Protection != PAGE_WRITECOPY && 3048 Protection != PAGE_EXECUTE && 3049 Protection != PAGE_EXECUTE_READ && 3050 Protection != PAGE_EXECUTE_READWRITE && 3051 Protection != PAGE_EXECUTE_WRITECOPY) 3052 { 3053 // 3054 // Fail 3055 // 3056 return STATUS_INVALID_PAGE_PROTECTION; 3057 } 3058 3059 // 3060 // Check if we came from user mode 3061 // 3062 if (PreviousMode != KernelMode) 3063 { 3064 // 3065 // Enter SEH for probing 3066 // 3067 _SEH2_TRY 3068 { 3069 // 3070 // Validate all outputs 3071 // 3072 ProbeForWritePointer(UnsafeBaseAddress); 3073 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect); 3074 ProbeForWriteUlong(UnsafeOldAccessProtection); 3075 3076 // 3077 // Capture them 3078 // 3079 BaseAddress = *UnsafeBaseAddress; 3080 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 3081 } 3082 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3083 { 3084 // 3085 // Get exception code 3086 // 3087 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3088 } 3089 _SEH2_END; 3090 } 3091 else 3092 { 3093 // 3094 // Capture directly 3095 // 3096 BaseAddress = *UnsafeBaseAddress; 3097 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 3098 } 3099 3100 // 3101 // Catch illegal base address 3102 // 3103 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 3104 3105 // 3106 // Catch illegal region size 3107 // 3108 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect) 3109 { 3110 // 3111 // Fail 3112 // 3113 return STATUS_INVALID_PARAMETER_3; 3114 } 3115 3116 // 3117 // 0 is also illegal 3118 // 3119 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3; 3120 3121 // 3122 // Get a reference to the process 3123 // 3124 Status = ObReferenceObjectByHandle(ProcessHandle, 3125 PROCESS_VM_OPERATION, 3126 PsProcessType, 3127 PreviousMode, 3128 (PVOID*)(&Process), 3129 NULL); 3130 if (!NT_SUCCESS(Status)) return Status; 3131 3132 // 3133 // Check if we should attach 3134 // 3135 if (CurrentProcess != Process) 3136 { 3137 // 3138 // Do it 3139 // 3140 KeStackAttachProcess(&Process->Pcb, &ApcState); 3141 Attached = TRUE; 3142 } 3143 3144 // 3145 // Do the actual work 3146 // 3147 Status = MiProtectVirtualMemory(Process, 3148 &BaseAddress, 3149 &NumberOfBytesToProtect, 3150 NewAccessProtection, 3151 &OldAccessProtection); 3152 3153 // 3154 // Detach if needed 3155 // 3156 if (Attached) KeUnstackDetachProcess(&ApcState); 3157 3158 // 3159 // Release reference 3160 // 3161 ObDereferenceObject(Process); 3162 3163 // 3164 // Enter SEH to return data 3165 // 3166 _SEH2_TRY 3167 { 3168 // 3169 // Return data to user 3170 // 3171 *UnsafeOldAccessProtection = OldAccessProtection; 3172 *UnsafeBaseAddress = BaseAddress; 3173 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect; 3174 } 3175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3176 { 3177 } 3178 _SEH2_END; 3179 3180 // 3181 // Return status 3182 // 3183 return Status; 3184 } 3185 3186 FORCEINLINE 3187 BOOLEAN 3188 MI_IS_LOCKED_VA( 3189 PMMPFN Pfn1, 3190 ULONG LockType) 3191 { 3192 // HACK until we have proper WSLIST support 3193 PMMWSLE Wsle = &Pfn1->Wsle; 3194 3195 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs)) 3196 return TRUE; 3197 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory)) 3198 return TRUE; 3199 3200 return FALSE; 3201 } 3202 3203 FORCEINLINE 3204 VOID 3205 MI_LOCK_VA( 3206 PMMPFN Pfn1, 3207 ULONG LockType) 3208 { 3209 // HACK until we have proper WSLIST support 3210 PMMWSLE Wsle = &Pfn1->Wsle; 3211 3212 if (!Wsle->u1.e1.LockedInWs && 3213 !Wsle->u1.e1.LockedInMemory) 3214 { 3215 MiReferenceProbedPageAndBumpLockCount(Pfn1); 3216 } 3217 3218 if (LockType & MAP_PROCESS) 3219 Wsle->u1.e1.LockedInWs = 1; 3220 if (LockType & MAP_SYSTEM) 3221 Wsle->u1.e1.LockedInMemory = 1; 3222 } 3223 3224 FORCEINLINE 3225 VOID 3226 MI_UNLOCK_VA( 3227 PMMPFN Pfn1, 3228 ULONG LockType) 3229 { 3230 // HACK until we have proper WSLIST support 3231 PMMWSLE Wsle = &Pfn1->Wsle; 3232 3233 if (LockType & MAP_PROCESS) 3234 Wsle->u1.e1.LockedInWs = 0; 3235 if (LockType & MAP_SYSTEM) 3236 Wsle->u1.e1.LockedInMemory = 0; 3237 3238 if (!Wsle->u1.e1.LockedInWs && 3239 !Wsle->u1.e1.LockedInMemory) 3240 { 3241 MiDereferencePfnAndDropLockCount(Pfn1); 3242 } 3243 } 3244 3245 static 3246 NTSTATUS 3247 MiCheckVadsForLockOperation( 3248 _Inout_ PVOID *BaseAddress, 3249 _Inout_ PSIZE_T RegionSize, 3250 _Inout_ PVOID *EndAddress) 3251 3252 { 3253 PMMVAD Vad; 3254 PVOID CurrentVa; 3255 3256 /* Get the base address and align the start address */ 3257 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize; 3258 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE); 3259 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE); 3260 3261 /* First loop and check all VADs */ 3262 CurrentVa = *BaseAddress; 3263 while (CurrentVa < *EndAddress) 3264 { 3265 /* Get VAD */ 3266 Vad = MiLocateAddress(CurrentVa); 3267 if (Vad == NULL) 3268 { 3269 /// FIXME: this might be a memory area for a section view... 3270 return STATUS_ACCESS_VIOLATION; 3271 } 3272 3273 /* Check VAD type */ 3274 if ((Vad->u.VadFlags.VadType != VadNone) && 3275 (Vad->u.VadFlags.VadType != VadImageMap) && 3276 (Vad->u.VadFlags.VadType != VadWriteWatch)) 3277 { 3278 *EndAddress = CurrentVa; 3279 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress; 3280 return STATUS_INCOMPATIBLE_FILE_MAP; 3281 } 3282 3283 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT); 3284 } 3285 3286 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress; 3287 return STATUS_SUCCESS; 3288 } 3289 3290 static 3291 NTSTATUS 3292 MiLockVirtualMemory( 3293 IN OUT PVOID *BaseAddress, 3294 IN OUT PSIZE_T RegionSize, 3295 IN ULONG MapType) 3296 { 3297 PEPROCESS CurrentProcess; 3298 PMMSUPPORT AddressSpace; 3299 PVOID CurrentVa, EndAddress; 3300 PMMPTE PointerPte, LastPte; 3301 PMMPDE PointerPde; 3302 #if (_MI_PAGING_LEVELS >= 3) 3303 PMMPDE PointerPpe; 3304 #endif 3305 #if (_MI_PAGING_LEVELS == 4) 3306 PMMPDE PointerPxe; 3307 #endif 3308 PMMPFN Pfn1; 3309 NTSTATUS Status, TempStatus; 3310 3311 /* Lock the address space */ 3312 AddressSpace = MmGetCurrentAddressSpace(); 3313 MmLockAddressSpace(AddressSpace); 3314 3315 /* Make sure we still have an address space */ 3316 CurrentProcess = PsGetCurrentProcess(); 3317 if (CurrentProcess->VmDeleted) 3318 { 3319 Status = STATUS_PROCESS_IS_TERMINATING; 3320 goto Cleanup; 3321 } 3322 3323 /* Check the VADs in the requested range */ 3324 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress); 3325 if (!NT_SUCCESS(Status)) 3326 { 3327 goto Cleanup; 3328 } 3329 3330 /* Enter SEH for probing */ 3331 _SEH2_TRY 3332 { 3333 /* Loop all pages and probe them */ 3334 CurrentVa = *BaseAddress; 3335 while (CurrentVa < EndAddress) 3336 { 3337 (void)(*(volatile CHAR*)CurrentVa); 3338 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE; 3339 } 3340 } 3341 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3342 { 3343 Status = _SEH2_GetExceptionCode(); 3344 goto Cleanup; 3345 } 3346 _SEH2_END; 3347 3348 /* All pages were accessible, since we hold the address space lock, nothing 3349 can be de-committed. Assume success for now. */ 3350 Status = STATUS_SUCCESS; 3351 3352 /* Get the PTE and PDE */ 3353 PointerPte = MiAddressToPte(*BaseAddress); 3354 PointerPde = MiAddressToPde(*BaseAddress); 3355 #if (_MI_PAGING_LEVELS >= 3) 3356 PointerPpe = MiAddressToPpe(*BaseAddress); 3357 #endif 3358 #if (_MI_PAGING_LEVELS == 4) 3359 PointerPxe = MiAddressToPxe(*BaseAddress); 3360 #endif 3361 3362 /* Get the last PTE */ 3363 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1)); 3364 3365 /* Lock the process working set */ 3366 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3367 3368 /* Loop the pages */ 3369 do 3370 { 3371 /* Check for a page that is not accessible */ 3372 while ( 3373 #if (_MI_PAGING_LEVELS == 4) 3374 (PointerPxe->u.Hard.Valid == 0) || 3375 #endif 3376 #if (_MI_PAGING_LEVELS >= 3) 3377 (PointerPpe->u.Hard.Valid == 0) || 3378 #endif 3379 (PointerPde->u.Hard.Valid == 0) || 3380 (PointerPte->u.Hard.Valid == 0)) 3381 { 3382 /* Release process working set */ 3383 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3384 3385 /* Access the page */ 3386 CurrentVa = MiPteToAddress(PointerPte); 3387 3388 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked 3389 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL); 3390 if (!NT_SUCCESS(TempStatus)) 3391 { 3392 // This should only happen, when remote backing storage is not accessible 3393 ASSERT(FALSE); 3394 Status = TempStatus; 3395 goto Cleanup; 3396 } 3397 3398 /* Lock the process working set */ 3399 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3400 } 3401 3402 /* Get the PFN */ 3403 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3404 ASSERT(Pfn1 != NULL); 3405 3406 /* Check the previous lock status */ 3407 if (MI_IS_LOCKED_VA(Pfn1, MapType)) 3408 { 3409 Status = STATUS_WAS_LOCKED; 3410 } 3411 3412 /* Lock it */ 3413 MI_LOCK_VA(Pfn1, MapType); 3414 3415 /* Go to the next PTE */ 3416 PointerPte++; 3417 3418 /* Check if we're on a PDE boundary */ 3419 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3420 #if (_MI_PAGING_LEVELS >= 3) 3421 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3422 #endif 3423 #if (_MI_PAGING_LEVELS == 4) 3424 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3425 #endif 3426 } while (PointerPte <= LastPte); 3427 3428 /* Release process working set */ 3429 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3430 3431 Cleanup: 3432 /* Unlock address space */ 3433 MmUnlockAddressSpace(AddressSpace); 3434 3435 return Status; 3436 } 3437 3438 NTSTATUS 3439 NTAPI 3440 NtLockVirtualMemory(IN HANDLE ProcessHandle, 3441 IN OUT PVOID *BaseAddress, 3442 IN OUT PSIZE_T NumberOfBytesToLock, 3443 IN ULONG MapType) 3444 { 3445 PEPROCESS Process; 3446 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3447 NTSTATUS Status; 3448 BOOLEAN Attached = FALSE; 3449 KAPC_STATE ApcState; 3450 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3451 PVOID CapturedBaseAddress; 3452 SIZE_T CapturedBytesToLock; 3453 PAGED_CODE(); 3454 3455 // 3456 // Validate flags 3457 // 3458 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 3459 { 3460 // 3461 // Invalid set of flags 3462 // 3463 return STATUS_INVALID_PARAMETER; 3464 } 3465 3466 // 3467 // At least one flag must be specified 3468 // 3469 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 3470 { 3471 // 3472 // No flag given 3473 // 3474 return STATUS_INVALID_PARAMETER; 3475 } 3476 3477 // 3478 // Enter SEH for probing 3479 // 3480 _SEH2_TRY 3481 { 3482 // 3483 // Validate output data 3484 // 3485 ProbeForWritePointer(BaseAddress); 3486 ProbeForWriteSize_t(NumberOfBytesToLock); 3487 3488 // 3489 // Capture it 3490 // 3491 CapturedBaseAddress = *BaseAddress; 3492 CapturedBytesToLock = *NumberOfBytesToLock; 3493 } 3494 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3495 { 3496 // 3497 // Get exception code 3498 // 3499 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3500 } 3501 _SEH2_END; 3502 3503 // 3504 // Catch illegal base address 3505 // 3506 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 3507 3508 // 3509 // Catch illegal region size 3510 // 3511 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock) 3512 { 3513 // 3514 // Fail 3515 // 3516 return STATUS_INVALID_PARAMETER; 3517 } 3518 3519 // 3520 // 0 is also illegal 3521 // 3522 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER; 3523 3524 // 3525 // Get a reference to the process 3526 // 3527 Status = ObReferenceObjectByHandle(ProcessHandle, 3528 PROCESS_VM_OPERATION, 3529 PsProcessType, 3530 PreviousMode, 3531 (PVOID*)(&Process), 3532 NULL); 3533 if (!NT_SUCCESS(Status)) return Status; 3534 3535 // 3536 // Check if this is is system-mapped 3537 // 3538 if (MapType & MAP_SYSTEM) 3539 { 3540 // 3541 // Check for required privilege 3542 // 3543 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 3544 { 3545 // 3546 // Fail: Don't have it 3547 // 3548 ObDereferenceObject(Process); 3549 return STATUS_PRIVILEGE_NOT_HELD; 3550 } 3551 } 3552 3553 // 3554 // Check if we should attach 3555 // 3556 if (CurrentProcess != Process) 3557 { 3558 // 3559 // Do it 3560 // 3561 KeStackAttachProcess(&Process->Pcb, &ApcState); 3562 Attached = TRUE; 3563 } 3564 3565 // 3566 // Call the internal function 3567 // 3568 Status = MiLockVirtualMemory(&CapturedBaseAddress, 3569 &CapturedBytesToLock, 3570 MapType); 3571 3572 // 3573 // Detach if needed 3574 // 3575 if (Attached) KeUnstackDetachProcess(&ApcState); 3576 3577 // 3578 // Release reference 3579 // 3580 ObDereferenceObject(Process); 3581 3582 // 3583 // Enter SEH to return data 3584 // 3585 _SEH2_TRY 3586 { 3587 // 3588 // Return data to user 3589 // 3590 *BaseAddress = CapturedBaseAddress; 3591 *NumberOfBytesToLock = CapturedBytesToLock; 3592 } 3593 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3594 { 3595 // 3596 // Get exception code 3597 // 3598 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3599 } 3600 _SEH2_END; 3601 3602 // 3603 // Return status 3604 // 3605 return Status; 3606 } 3607 3608 3609 static 3610 NTSTATUS 3611 MiUnlockVirtualMemory( 3612 IN OUT PVOID *BaseAddress, 3613 IN OUT PSIZE_T RegionSize, 3614 IN ULONG MapType) 3615 { 3616 PEPROCESS CurrentProcess; 3617 PMMSUPPORT AddressSpace; 3618 PVOID EndAddress; 3619 PMMPTE PointerPte, LastPte; 3620 PMMPDE PointerPde; 3621 #if (_MI_PAGING_LEVELS >= 3) 3622 PMMPDE PointerPpe; 3623 #endif 3624 #if (_MI_PAGING_LEVELS == 4) 3625 PMMPDE PointerPxe; 3626 #endif 3627 PMMPFN Pfn1; 3628 NTSTATUS Status; 3629 3630 /* Lock the address space */ 3631 AddressSpace = MmGetCurrentAddressSpace(); 3632 MmLockAddressSpace(AddressSpace); 3633 3634 /* Make sure we still have an address space */ 3635 CurrentProcess = PsGetCurrentProcess(); 3636 if (CurrentProcess->VmDeleted) 3637 { 3638 Status = STATUS_PROCESS_IS_TERMINATING; 3639 goto Cleanup; 3640 } 3641 3642 /* Check the VADs in the requested range */ 3643 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress); 3644 3645 /* Note: only bail out, if we hit an area without a VAD. If we hit an 3646 incompatible VAD we continue, like Windows does */ 3647 if (Status == STATUS_ACCESS_VIOLATION) 3648 { 3649 Status = STATUS_NOT_LOCKED; 3650 goto Cleanup; 3651 } 3652 3653 /* Get the PTE and PDE */ 3654 PointerPte = MiAddressToPte(*BaseAddress); 3655 PointerPde = MiAddressToPde(*BaseAddress); 3656 #if (_MI_PAGING_LEVELS >= 3) 3657 PointerPpe = MiAddressToPpe(*BaseAddress); 3658 #endif 3659 #if (_MI_PAGING_LEVELS == 4) 3660 PointerPxe = MiAddressToPxe(*BaseAddress); 3661 #endif 3662 3663 /* Get the last PTE */ 3664 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1)); 3665 3666 /* Lock the process working set */ 3667 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3668 3669 /* Loop the pages */ 3670 do 3671 { 3672 /* Check for a page that is not present */ 3673 if ( 3674 #if (_MI_PAGING_LEVELS == 4) 3675 (PointerPxe->u.Hard.Valid == 0) || 3676 #endif 3677 #if (_MI_PAGING_LEVELS >= 3) 3678 (PointerPpe->u.Hard.Valid == 0) || 3679 #endif 3680 (PointerPde->u.Hard.Valid == 0) || 3681 (PointerPte->u.Hard.Valid == 0)) 3682 { 3683 /* Remember it, but keep going */ 3684 Status = STATUS_NOT_LOCKED; 3685 } 3686 else 3687 { 3688 /* Get the PFN */ 3689 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3690 ASSERT(Pfn1 != NULL); 3691 3692 /* Check if all of the requested locks are present */ 3693 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) || 3694 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS))) 3695 { 3696 /* Remember it, but keep going */ 3697 Status = STATUS_NOT_LOCKED; 3698 3699 /* Check if no lock is present */ 3700 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM)) 3701 { 3702 DPRINT1("FIXME: Should remove the page from WS\n"); 3703 } 3704 } 3705 } 3706 3707 /* Go to the next PTE */ 3708 PointerPte++; 3709 3710 /* Check if we're on a PDE boundary */ 3711 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3712 #if (_MI_PAGING_LEVELS >= 3) 3713 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3714 #endif 3715 #if (_MI_PAGING_LEVELS == 4) 3716 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3717 #endif 3718 } while (PointerPte <= LastPte); 3719 3720 /* Check if we hit a page that was not locked */ 3721 if (Status == STATUS_NOT_LOCKED) 3722 { 3723 goto CleanupWithWsLock; 3724 } 3725 3726 /* All pages in the region were locked, so unlock them all */ 3727 3728 /* Get the PTE and PDE */ 3729 PointerPte = MiAddressToPte(*BaseAddress); 3730 PointerPde = MiAddressToPde(*BaseAddress); 3731 #if (_MI_PAGING_LEVELS >= 3) 3732 PointerPpe = MiAddressToPpe(*BaseAddress); 3733 #endif 3734 #if (_MI_PAGING_LEVELS == 4) 3735 PointerPxe = MiAddressToPxe(*BaseAddress); 3736 #endif 3737 3738 /* Loop the pages */ 3739 do 3740 { 3741 /* Unlock it */ 3742 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte)); 3743 MI_UNLOCK_VA(Pfn1, MapType); 3744 3745 /* Go to the next PTE */ 3746 PointerPte++; 3747 3748 /* Check if we're on a PDE boundary */ 3749 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++; 3750 #if (_MI_PAGING_LEVELS >= 3) 3751 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++; 3752 #endif 3753 #if (_MI_PAGING_LEVELS == 4) 3754 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++; 3755 #endif 3756 } while (PointerPte <= LastPte); 3757 3758 /* Everything is done */ 3759 Status = STATUS_SUCCESS; 3760 3761 CleanupWithWsLock: 3762 3763 /* Release process working set */ 3764 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); 3765 3766 Cleanup: 3767 /* Unlock address space */ 3768 MmUnlockAddressSpace(AddressSpace); 3769 3770 return Status; 3771 } 3772 3773 3774 NTSTATUS 3775 NTAPI 3776 NtUnlockVirtualMemory(IN HANDLE ProcessHandle, 3777 IN OUT PVOID *BaseAddress, 3778 IN OUT PSIZE_T NumberOfBytesToUnlock, 3779 IN ULONG MapType) 3780 { 3781 PEPROCESS Process; 3782 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 3783 NTSTATUS Status; 3784 BOOLEAN Attached = FALSE; 3785 KAPC_STATE ApcState; 3786 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3787 PVOID CapturedBaseAddress; 3788 SIZE_T CapturedBytesToUnlock; 3789 PAGED_CODE(); 3790 3791 // 3792 // Validate flags 3793 // 3794 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 3795 { 3796 // 3797 // Invalid set of flags 3798 // 3799 return STATUS_INVALID_PARAMETER; 3800 } 3801 3802 // 3803 // At least one flag must be specified 3804 // 3805 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 3806 { 3807 // 3808 // No flag given 3809 // 3810 return STATUS_INVALID_PARAMETER; 3811 } 3812 3813 // 3814 // Enter SEH for probing 3815 // 3816 _SEH2_TRY 3817 { 3818 // 3819 // Validate output data 3820 // 3821 ProbeForWritePointer(BaseAddress); 3822 ProbeForWriteSize_t(NumberOfBytesToUnlock); 3823 3824 // 3825 // Capture it 3826 // 3827 CapturedBaseAddress = *BaseAddress; 3828 CapturedBytesToUnlock = *NumberOfBytesToUnlock; 3829 } 3830 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3831 { 3832 // 3833 // Get exception code 3834 // 3835 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3836 } 3837 _SEH2_END; 3838 3839 // 3840 // Catch illegal base address 3841 // 3842 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 3843 3844 // 3845 // Catch illegal region size 3846 // 3847 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock) 3848 { 3849 // 3850 // Fail 3851 // 3852 return STATUS_INVALID_PARAMETER; 3853 } 3854 3855 // 3856 // 0 is also illegal 3857 // 3858 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER; 3859 3860 // 3861 // Get a reference to the process 3862 // 3863 Status = ObReferenceObjectByHandle(ProcessHandle, 3864 PROCESS_VM_OPERATION, 3865 PsProcessType, 3866 PreviousMode, 3867 (PVOID*)(&Process), 3868 NULL); 3869 if (!NT_SUCCESS(Status)) return Status; 3870 3871 // 3872 // Check if this is is system-mapped 3873 // 3874 if (MapType & MAP_SYSTEM) 3875 { 3876 // 3877 // Check for required privilege 3878 // 3879 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 3880 { 3881 // 3882 // Fail: Don't have it 3883 // 3884 ObDereferenceObject(Process); 3885 return STATUS_PRIVILEGE_NOT_HELD; 3886 } 3887 } 3888 3889 // 3890 // Check if we should attach 3891 // 3892 if (CurrentProcess != Process) 3893 { 3894 // 3895 // Do it 3896 // 3897 KeStackAttachProcess(&Process->Pcb, &ApcState); 3898 Attached = TRUE; 3899 } 3900 3901 // 3902 // Call the internal function 3903 // 3904 Status = MiUnlockVirtualMemory(&CapturedBaseAddress, 3905 &CapturedBytesToUnlock, 3906 MapType); 3907 3908 // 3909 // Detach if needed 3910 // 3911 if (Attached) KeUnstackDetachProcess(&ApcState); 3912 3913 // 3914 // Release reference 3915 // 3916 ObDereferenceObject(Process); 3917 3918 // 3919 // Enter SEH to return data 3920 // 3921 _SEH2_TRY 3922 { 3923 // 3924 // Return data to user 3925 // 3926 *BaseAddress = CapturedBaseAddress; 3927 *NumberOfBytesToUnlock = CapturedBytesToUnlock; 3928 } 3929 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3930 { 3931 // 3932 // Get exception code 3933 // 3934 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3935 } 3936 _SEH2_END; 3937 3938 // 3939 // Return status 3940 // 3941 return STATUS_SUCCESS; 3942 } 3943 3944 NTSTATUS 3945 NTAPI 3946 NtFlushVirtualMemory(IN HANDLE ProcessHandle, 3947 IN OUT PVOID *BaseAddress, 3948 IN OUT PSIZE_T NumberOfBytesToFlush, 3949 OUT PIO_STATUS_BLOCK IoStatusBlock) 3950 { 3951 PEPROCESS Process; 3952 NTSTATUS Status; 3953 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 3954 PVOID CapturedBaseAddress; 3955 SIZE_T CapturedBytesToFlush; 3956 IO_STATUS_BLOCK LocalStatusBlock; 3957 PAGED_CODE(); 3958 3959 // 3960 // Check if we came from user mode 3961 // 3962 if (PreviousMode != KernelMode) 3963 { 3964 // 3965 // Enter SEH for probing 3966 // 3967 _SEH2_TRY 3968 { 3969 // 3970 // Validate all outputs 3971 // 3972 ProbeForWritePointer(BaseAddress); 3973 ProbeForWriteSize_t(NumberOfBytesToFlush); 3974 ProbeForWriteIoStatusBlock(IoStatusBlock); 3975 3976 // 3977 // Capture them 3978 // 3979 CapturedBaseAddress = *BaseAddress; 3980 CapturedBytesToFlush = *NumberOfBytesToFlush; 3981 } 3982 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 3983 { 3984 // 3985 // Get exception code 3986 // 3987 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 3988 } 3989 _SEH2_END; 3990 } 3991 else 3992 { 3993 // 3994 // Capture directly 3995 // 3996 CapturedBaseAddress = *BaseAddress; 3997 CapturedBytesToFlush = *NumberOfBytesToFlush; 3998 } 3999 4000 // 4001 // Catch illegal base address 4002 // 4003 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 4004 4005 // 4006 // Catch illegal region size 4007 // 4008 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush) 4009 { 4010 // 4011 // Fail 4012 // 4013 return STATUS_INVALID_PARAMETER; 4014 } 4015 4016 // 4017 // Get a reference to the process 4018 // 4019 Status = ObReferenceObjectByHandle(ProcessHandle, 4020 PROCESS_VM_OPERATION, 4021 PsProcessType, 4022 PreviousMode, 4023 (PVOID*)(&Process), 4024 NULL); 4025 if (!NT_SUCCESS(Status)) return Status; 4026 4027 // 4028 // Do it 4029 // 4030 Status = MmFlushVirtualMemory(Process, 4031 &CapturedBaseAddress, 4032 &CapturedBytesToFlush, 4033 &LocalStatusBlock); 4034 4035 // 4036 // Release reference 4037 // 4038 ObDereferenceObject(Process); 4039 4040 // 4041 // Enter SEH to return data 4042 // 4043 _SEH2_TRY 4044 { 4045 // 4046 // Return data to user 4047 // 4048 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); 4049 *NumberOfBytesToFlush = 0; 4050 *IoStatusBlock = LocalStatusBlock; 4051 } 4052 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4053 { 4054 } 4055 _SEH2_END; 4056 4057 // 4058 // Return status 4059 // 4060 return Status; 4061 } 4062 4063 /* 4064 * @unimplemented 4065 */ 4066 NTSTATUS 4067 NTAPI 4068 NtGetWriteWatch(IN HANDLE ProcessHandle, 4069 IN ULONG Flags, 4070 IN PVOID BaseAddress, 4071 IN SIZE_T RegionSize, 4072 IN PVOID *UserAddressArray, 4073 OUT PULONG_PTR EntriesInUserAddressArray, 4074 OUT PULONG Granularity) 4075 { 4076 PEPROCESS Process; 4077 NTSTATUS Status; 4078 PVOID EndAddress; 4079 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4080 ULONG_PTR CapturedEntryCount; 4081 PAGED_CODE(); 4082 4083 // 4084 // Check if we came from user mode 4085 // 4086 if (PreviousMode != KernelMode) 4087 { 4088 // 4089 // Enter SEH for probing 4090 // 4091 _SEH2_TRY 4092 { 4093 // 4094 // Catch illegal base address 4095 // 4096 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2); 4097 4098 // 4099 // Catch illegal region size 4100 // 4101 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 4102 { 4103 // 4104 // Fail 4105 // 4106 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3); 4107 } 4108 4109 // 4110 // Validate all data 4111 // 4112 ProbeForWriteSize_t(EntriesInUserAddressArray); 4113 ProbeForWriteUlong(Granularity); 4114 4115 // 4116 // Capture them 4117 // 4118 CapturedEntryCount = *EntriesInUserAddressArray; 4119 4120 // 4121 // Must have a count 4122 // 4123 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5); 4124 4125 // 4126 // Can't be larger than the maximum 4127 // 4128 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR))) 4129 { 4130 // 4131 // Fail 4132 // 4133 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5); 4134 } 4135 4136 // 4137 // Probe the actual array 4138 // 4139 ProbeForWrite(UserAddressArray, 4140 CapturedEntryCount * sizeof(PVOID), 4141 sizeof(PVOID)); 4142 } 4143 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4144 { 4145 // 4146 // Get exception code 4147 // 4148 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4149 } 4150 _SEH2_END; 4151 } 4152 else 4153 { 4154 // 4155 // Capture directly 4156 // 4157 CapturedEntryCount = *EntriesInUserAddressArray; 4158 ASSERT(CapturedEntryCount != 0); 4159 } 4160 4161 // 4162 // Check if this is a local request 4163 // 4164 if (ProcessHandle == NtCurrentProcess()) 4165 { 4166 // 4167 // No need to reference the process 4168 // 4169 Process = PsGetCurrentProcess(); 4170 } 4171 else 4172 { 4173 // 4174 // Reference the target 4175 // 4176 Status = ObReferenceObjectByHandle(ProcessHandle, 4177 PROCESS_VM_OPERATION, 4178 PsProcessType, 4179 PreviousMode, 4180 (PVOID *)&Process, 4181 NULL); 4182 if (!NT_SUCCESS(Status)) return Status; 4183 } 4184 4185 // 4186 // Compute the last address and validate it 4187 // 4188 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 4189 if (BaseAddress > EndAddress) 4190 { 4191 // 4192 // Fail 4193 // 4194 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4195 return STATUS_INVALID_PARAMETER_4; 4196 } 4197 4198 // 4199 // Oops :( 4200 // 4201 UNIMPLEMENTED; 4202 4203 // 4204 // Dereference if needed 4205 // 4206 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4207 4208 // 4209 // Enter SEH to return data 4210 // 4211 _SEH2_TRY 4212 { 4213 // 4214 // Return data to user 4215 // 4216 *EntriesInUserAddressArray = 0; 4217 *Granularity = PAGE_SIZE; 4218 } 4219 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4220 { 4221 // 4222 // Get exception code 4223 // 4224 Status = _SEH2_GetExceptionCode(); 4225 } 4226 _SEH2_END; 4227 4228 // 4229 // Return success 4230 // 4231 return STATUS_SUCCESS; 4232 } 4233 4234 /* 4235 * @unimplemented 4236 */ 4237 NTSTATUS 4238 NTAPI 4239 NtResetWriteWatch(IN HANDLE ProcessHandle, 4240 IN PVOID BaseAddress, 4241 IN SIZE_T RegionSize) 4242 { 4243 PVOID EndAddress; 4244 PEPROCESS Process; 4245 NTSTATUS Status; 4246 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 4247 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); 4248 4249 // 4250 // Catch illegal base address 4251 // 4252 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 4253 4254 // 4255 // Catch illegal region size 4256 // 4257 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 4258 { 4259 // 4260 // Fail 4261 // 4262 return STATUS_INVALID_PARAMETER_3; 4263 } 4264 4265 // 4266 // Check if this is a local request 4267 // 4268 if (ProcessHandle == NtCurrentProcess()) 4269 { 4270 // 4271 // No need to reference the process 4272 // 4273 Process = PsGetCurrentProcess(); 4274 } 4275 else 4276 { 4277 // 4278 // Reference the target 4279 // 4280 Status = ObReferenceObjectByHandle(ProcessHandle, 4281 PROCESS_VM_OPERATION, 4282 PsProcessType, 4283 PreviousMode, 4284 (PVOID *)&Process, 4285 NULL); 4286 if (!NT_SUCCESS(Status)) return Status; 4287 } 4288 4289 // 4290 // Compute the last address and validate it 4291 // 4292 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 4293 if (BaseAddress > EndAddress) 4294 { 4295 // 4296 // Fail 4297 // 4298 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4299 return STATUS_INVALID_PARAMETER_3; 4300 } 4301 4302 // 4303 // Oops :( 4304 // 4305 UNIMPLEMENTED; 4306 4307 // 4308 // Dereference if needed 4309 // 4310 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4311 4312 // 4313 // Return success 4314 // 4315 return STATUS_SUCCESS; 4316 } 4317 4318 NTSTATUS 4319 NTAPI 4320 NtQueryVirtualMemory(IN HANDLE ProcessHandle, 4321 IN PVOID BaseAddress, 4322 IN MEMORY_INFORMATION_CLASS MemoryInformationClass, 4323 OUT PVOID MemoryInformation, 4324 IN SIZE_T MemoryInformationLength, 4325 OUT PSIZE_T ReturnLength) 4326 { 4327 NTSTATUS Status = STATUS_SUCCESS; 4328 KPROCESSOR_MODE PreviousMode; 4329 4330 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress); 4331 4332 /* Bail out if the address is invalid */ 4333 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 4334 4335 /* Probe return buffer */ 4336 PreviousMode = ExGetPreviousMode(); 4337 if (PreviousMode != KernelMode) 4338 { 4339 _SEH2_TRY 4340 { 4341 ProbeForWrite(MemoryInformation, 4342 MemoryInformationLength, 4343 sizeof(ULONG_PTR)); 4344 4345 if (ReturnLength) ProbeForWriteSize_t(ReturnLength); 4346 } 4347 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4348 { 4349 Status = _SEH2_GetExceptionCode(); 4350 } 4351 _SEH2_END; 4352 4353 if (!NT_SUCCESS(Status)) 4354 { 4355 return Status; 4356 } 4357 } 4358 4359 switch(MemoryInformationClass) 4360 { 4361 case MemoryBasicInformation: 4362 /* Validate the size information of the class */ 4363 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) 4364 { 4365 /* The size is invalid */ 4366 return STATUS_INFO_LENGTH_MISMATCH; 4367 } 4368 Status = MiQueryMemoryBasicInformation(ProcessHandle, 4369 BaseAddress, 4370 MemoryInformation, 4371 MemoryInformationLength, 4372 ReturnLength); 4373 break; 4374 4375 case MemorySectionName: 4376 /* Validate the size information of the class */ 4377 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME)) 4378 { 4379 /* The size is invalid */ 4380 return STATUS_INFO_LENGTH_MISMATCH; 4381 } 4382 Status = MiQueryMemorySectionName(ProcessHandle, 4383 BaseAddress, 4384 MemoryInformation, 4385 MemoryInformationLength, 4386 ReturnLength); 4387 break; 4388 case MemoryWorkingSetList: 4389 case MemoryBasicVlmInformation: 4390 default: 4391 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass); 4392 break; 4393 } 4394 4395 return Status; 4396 } 4397 4398 /* 4399 * @implemented 4400 */ 4401 NTSTATUS 4402 NTAPI 4403 NtAllocateVirtualMemory(IN HANDLE ProcessHandle, 4404 IN OUT PVOID* UBaseAddress, 4405 IN ULONG_PTR ZeroBits, 4406 IN OUT PSIZE_T URegionSize, 4407 IN ULONG AllocationType, 4408 IN ULONG Protect) 4409 { 4410 PEPROCESS Process; 4411 PMEMORY_AREA MemoryArea; 4412 PMMVAD Vad = NULL, FoundVad; 4413 NTSTATUS Status; 4414 PMMSUPPORT AddressSpace; 4415 PVOID PBaseAddress; 4416 ULONG_PTR PRegionSize, StartingAddress, EndingAddress; 4417 ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS; 4418 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 4419 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 4420 PETHREAD CurrentThread = PsGetCurrentThread(); 4421 KAPC_STATE ApcState; 4422 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0; 4423 BOOLEAN Attached = FALSE, ChangeProtection = FALSE; 4424 MMPTE TempPte; 4425 PMMPTE PointerPte, LastPte; 4426 PMMPDE PointerPde; 4427 TABLE_SEARCH_RESULT Result; 4428 PAGED_CODE(); 4429 4430 /* Check for valid Zero bits */ 4431 if (ZeroBits > MI_MAX_ZERO_BITS) 4432 { 4433 DPRINT1("Too many zero bits\n"); 4434 return STATUS_INVALID_PARAMETER_3; 4435 } 4436 4437 /* Check for valid Allocation Types */ 4438 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL | 4439 MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES))) 4440 { 4441 DPRINT1("Invalid Allocation Type\n"); 4442 return STATUS_INVALID_PARAMETER_5; 4443 } 4444 4445 /* Check for at least one of these Allocation Types to be set */ 4446 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET))) 4447 { 4448 DPRINT1("No memory allocation base type\n"); 4449 return STATUS_INVALID_PARAMETER_5; 4450 } 4451 4452 /* MEM_RESET is an exclusive flag, make sure that is valid too */ 4453 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET)) 4454 { 4455 DPRINT1("Invalid use of MEM_RESET\n"); 4456 return STATUS_INVALID_PARAMETER_5; 4457 } 4458 4459 /* Check if large pages are being used */ 4460 if (AllocationType & MEM_LARGE_PAGES) 4461 { 4462 /* Large page allocations MUST be committed */ 4463 if (!(AllocationType & MEM_COMMIT)) 4464 { 4465 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n"); 4466 return STATUS_INVALID_PARAMETER_5; 4467 } 4468 4469 /* These flags are not allowed with large page allocations */ 4470 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH)) 4471 { 4472 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n"); 4473 return STATUS_INVALID_PARAMETER_5; 4474 } 4475 } 4476 4477 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */ 4478 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE)) 4479 { 4480 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n"); 4481 return STATUS_INVALID_PARAMETER_5; 4482 } 4483 4484 /* Check for valid MEM_PHYSICAL usage */ 4485 if (AllocationType & MEM_PHYSICAL) 4486 { 4487 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */ 4488 if (!(AllocationType & MEM_RESERVE)) 4489 { 4490 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n"); 4491 return STATUS_INVALID_PARAMETER_5; 4492 } 4493 4494 /* Only these flags are allowed with MEM_PHYSIAL */ 4495 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL)) 4496 { 4497 DPRINT1("Using illegal flags with MEM_PHYSICAL\n"); 4498 return STATUS_INVALID_PARAMETER_5; 4499 } 4500 4501 /* Then make sure PAGE_READWRITE is used */ 4502 if (Protect != PAGE_READWRITE) 4503 { 4504 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n"); 4505 return STATUS_INVALID_PARAMETER_6; 4506 } 4507 } 4508 4509 /* Calculate the protection mask and make sure it's valid */ 4510 ProtectionMask = MiMakeProtectionMask(Protect); 4511 if (ProtectionMask == MM_INVALID_PROTECTION) 4512 { 4513 DPRINT1("Invalid protection mask\n"); 4514 return STATUS_INVALID_PAGE_PROTECTION; 4515 } 4516 4517 /* Enter SEH */ 4518 _SEH2_TRY 4519 { 4520 /* Check for user-mode parameters */ 4521 if (PreviousMode != KernelMode) 4522 { 4523 /* Make sure they are writable */ 4524 ProbeForWritePointer(UBaseAddress); 4525 ProbeForWriteSize_t(URegionSize); 4526 } 4527 4528 /* Capture their values */ 4529 PBaseAddress = *UBaseAddress; 4530 PRegionSize = *URegionSize; 4531 } 4532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4533 { 4534 /* Return the exception code */ 4535 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 4536 } 4537 _SEH2_END; 4538 4539 /* Make sure the allocation isn't past the VAD area */ 4540 if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS) 4541 { 4542 DPRINT1("Virtual allocation base above User Space\n"); 4543 return STATUS_INVALID_PARAMETER_2; 4544 } 4545 4546 /* Make sure the allocation wouldn't overflow past the VAD area */ 4547 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize) 4548 { 4549 DPRINT1("Region size would overflow into kernel-memory\n"); 4550 return STATUS_INVALID_PARAMETER_4; 4551 } 4552 4553 /* Make sure there's a size specified */ 4554 if (!PRegionSize) 4555 { 4556 DPRINT1("Region size is invalid (zero)\n"); 4557 return STATUS_INVALID_PARAMETER_4; 4558 } 4559 4560 // 4561 // If this is for the current process, just use PsGetCurrentProcess 4562 // 4563 if (ProcessHandle == NtCurrentProcess()) 4564 { 4565 Process = CurrentProcess; 4566 } 4567 else 4568 { 4569 // 4570 // Otherwise, reference the process with VM rights and attach to it if 4571 // this isn't the current process. We must attach because we'll be touching 4572 // PTEs and PDEs that belong to user-mode memory, and also touching the 4573 // Working Set which is stored in Hyperspace. 4574 // 4575 Status = ObReferenceObjectByHandle(ProcessHandle, 4576 PROCESS_VM_OPERATION, 4577 PsProcessType, 4578 PreviousMode, 4579 (PVOID*)&Process, 4580 NULL); 4581 if (!NT_SUCCESS(Status)) return Status; 4582 if (CurrentProcess != Process) 4583 { 4584 KeStackAttachProcess(&Process->Pcb, &ApcState); 4585 Attached = TRUE; 4586 } 4587 } 4588 4589 DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n", 4590 Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect); 4591 4592 // 4593 // Check for large page allocations and make sure that the required privilege 4594 // is being held, before attempting to handle them. 4595 // 4596 if ((AllocationType & MEM_LARGE_PAGES) && 4597 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))) 4598 { 4599 /* Fail without it */ 4600 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n"); 4601 Status = STATUS_PRIVILEGE_NOT_HELD; 4602 goto FailPathNoLock; 4603 } 4604 4605 // 4606 // Fail on the things we don't yet support 4607 // 4608 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES) 4609 { 4610 DPRINT1("MEM_LARGE_PAGES not supported\n"); 4611 Status = STATUS_INVALID_PARAMETER; 4612 goto FailPathNoLock; 4613 } 4614 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL) 4615 { 4616 DPRINT1("MEM_PHYSICAL not supported\n"); 4617 Status = STATUS_INVALID_PARAMETER; 4618 goto FailPathNoLock; 4619 } 4620 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH) 4621 { 4622 DPRINT1("MEM_WRITE_WATCH not supported\n"); 4623 Status = STATUS_INVALID_PARAMETER; 4624 goto FailPathNoLock; 4625 } 4626 4627 // 4628 // Check if the caller is reserving memory, or committing memory and letting 4629 // us pick the base address 4630 // 4631 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE)) 4632 { 4633 // 4634 // Do not allow COPY_ON_WRITE through this API 4635 // 4636 if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) 4637 { 4638 DPRINT1("Copy on write not allowed through this path\n"); 4639 Status = STATUS_INVALID_PAGE_PROTECTION; 4640 goto FailPathNoLock; 4641 } 4642 4643 // 4644 // Does the caller have an address in mind, or is this a blind commit? 4645 // 4646 if (!PBaseAddress) 4647 { 4648 // 4649 // This is a blind commit, all we need is the region size 4650 // 4651 PRegionSize = ROUND_TO_PAGES(PRegionSize); 4652 EndingAddress = 0; 4653 StartingAddress = 0; 4654 4655 // 4656 // Check if ZeroBits were specified 4657 // 4658 if (ZeroBits != 0) 4659 { 4660 // 4661 // Calculate the highest address and check if it's valid 4662 // 4663 HighestAddress = MAXULONG_PTR >> ZeroBits; 4664 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) 4665 { 4666 Status = STATUS_INVALID_PARAMETER_3; 4667 goto FailPathNoLock; 4668 } 4669 } 4670 } 4671 else 4672 { 4673 // 4674 // This is a reservation, so compute the starting address on the 4675 // expected 64KB granularity, and see where the ending address will 4676 // fall based on the aligned address and the passed in region size 4677 // 4678 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 4679 PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K); 4680 StartingAddress = (ULONG_PTR)PBaseAddress; 4681 } 4682 4683 // 4684 // Allocate and initialize the VAD 4685 // 4686 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); 4687 if (Vad == NULL) 4688 { 4689 DPRINT1("Failed to allocate a VAD!\n"); 4690 Status = STATUS_INSUFFICIENT_RESOURCES; 4691 goto FailPathNoLock; 4692 } 4693 4694 RtlZeroMemory(Vad, sizeof(MMVAD_LONG)); 4695 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1; 4696 Vad->u.VadFlags.Protection = ProtectionMask; 4697 Vad->u.VadFlags.PrivateMemory = 1; 4698 Vad->ControlArea = NULL; // For Memory-Area hack 4699 4700 // 4701 // Insert the VAD 4702 // 4703 Status = MiInsertVadEx(Vad, 4704 &StartingAddress, 4705 PRegionSize, 4706 HighestAddress, 4707 MM_VIRTMEM_GRANULARITY, 4708 AllocationType); 4709 if (!NT_SUCCESS(Status)) 4710 { 4711 DPRINT1("Failed to insert the VAD!\n"); 4712 goto FailPathNoLock; 4713 } 4714 4715 // 4716 // Detach and dereference the target process if 4717 // it was different from the current process 4718 // 4719 if (Attached) KeUnstackDetachProcess(&ApcState); 4720 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 4721 4722 // 4723 // Use SEH to write back the base address and the region size. In the case 4724 // of an exception, we do not return back the exception code, as the memory 4725 // *has* been allocated. The caller would now have to call VirtualQuery 4726 // or do some other similar trick to actually find out where its memory 4727 // allocation ended up 4728 // 4729 _SEH2_TRY 4730 { 4731 *URegionSize = PRegionSize; 4732 *UBaseAddress = (PVOID)StartingAddress; 4733 } 4734 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4735 { 4736 // 4737 // Ignore exception! 4738 // 4739 } 4740 _SEH2_END; 4741 DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress); 4742 return STATUS_SUCCESS; 4743 } 4744 4745 // 4746 // This is a MEM_COMMIT on top of an existing address which must have been 4747 // MEM_RESERVED already. Compute the start and ending base addresses based 4748 // on the user input, and then compute the actual region size once all the 4749 // alignments have been done. 4750 // 4751 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1)); 4752 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 4753 PRegionSize = EndingAddress - StartingAddress + 1; 4754 4755 // 4756 // Lock the address space and make sure the process isn't already dead 4757 // 4758 AddressSpace = MmGetCurrentAddressSpace(); 4759 MmLockAddressSpace(AddressSpace); 4760 if (Process->VmDeleted) 4761 { 4762 DPRINT1("Process is dying\n"); 4763 Status = STATUS_PROCESS_IS_TERMINATING; 4764 goto FailPath; 4765 } 4766 4767 // 4768 // Get the VAD for this address range, and make sure it exists 4769 // 4770 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 4771 EndingAddress >> PAGE_SHIFT, 4772 &Process->VadRoot, 4773 (PMMADDRESS_NODE*)&FoundVad); 4774 if (Result != TableFoundNode) 4775 { 4776 DPRINT1("Could not find a VAD for this allocation\n"); 4777 Status = STATUS_CONFLICTING_ADDRESSES; 4778 goto FailPath; 4779 } 4780 4781 if ((AllocationType & MEM_RESET) == MEM_RESET) 4782 { 4783 /// @todo HACK: pretend success 4784 DPRINT("MEM_RESET not supported\n"); 4785 Status = STATUS_SUCCESS; 4786 goto FailPath; 4787 } 4788 4789 // 4790 // These kinds of VADs are illegal for this Windows function when trying to 4791 // commit an existing range 4792 // 4793 if ((FoundVad->u.VadFlags.VadType == VadAwe) || 4794 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 4795 (FoundVad->u.VadFlags.VadType == VadLargePages)) 4796 { 4797 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n"); 4798 Status = STATUS_CONFLICTING_ADDRESSES; 4799 goto FailPath; 4800 } 4801 4802 // 4803 // Make sure that this address range actually fits within the VAD for it 4804 // 4805 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) || 4806 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn)) 4807 { 4808 DPRINT1("Address range does not fit into the VAD\n"); 4809 Status = STATUS_CONFLICTING_ADDRESSES; 4810 goto FailPath; 4811 } 4812 4813 // 4814 // Make sure this is an ARM3 section 4815 // 4816 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress)); 4817 ASSERT(MemoryArea != NULL); 4818 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3) 4819 { 4820 DPRINT1("Illegal commit of non-ARM3 section!\n"); 4821 Status = STATUS_ALREADY_COMMITTED; 4822 goto FailPath; 4823 } 4824 4825 // Is this a previously reserved section being committed? If so, enter the 4826 // special section path 4827 // 4828 if (FoundVad->u.VadFlags.PrivateMemory == FALSE) 4829 { 4830 // 4831 // You cannot commit large page sections through this API 4832 // 4833 if (FoundVad->u.VadFlags.VadType == VadLargePageSection) 4834 { 4835 DPRINT1("Large page sections cannot be VirtualAlloc'd\n"); 4836 Status = STATUS_INVALID_PAGE_PROTECTION; 4837 goto FailPath; 4838 } 4839 4840 // 4841 // You can only use caching flags on a rotate VAD 4842 // 4843 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) && 4844 (FoundVad->u.VadFlags.VadType != VadRotatePhysical)) 4845 { 4846 DPRINT1("Cannot use caching flags with anything but rotate VADs\n"); 4847 Status = STATUS_INVALID_PAGE_PROTECTION; 4848 goto FailPath; 4849 } 4850 4851 // 4852 // We should make sure that the section's permissions aren't being 4853 // messed with 4854 // 4855 if (FoundVad->u.VadFlags.NoChange) 4856 { 4857 // 4858 // Make sure it's okay to touch it 4859 // Note: The Windows 2003 kernel has a bug here, passing the 4860 // unaligned base address together with the aligned size, 4861 // potentially covering a region larger than the actual allocation. 4862 // Might be exposed through NtGdiCreateDIBSection w/ section handle 4863 // For now we keep this behavior. 4864 // TODO: analyze possible implications, create test case 4865 // 4866 Status = MiCheckSecuredVad(FoundVad, 4867 PBaseAddress, 4868 PRegionSize, 4869 ProtectionMask); 4870 if (!NT_SUCCESS(Status)) 4871 { 4872 DPRINT1("Secured VAD being messed around with\n"); 4873 goto FailPath; 4874 } 4875 } 4876 4877 // 4878 // ARM3 does not support file-backed sections, only shared memory 4879 // 4880 ASSERT(FoundVad->ControlArea->FilePointer == NULL); 4881 4882 // 4883 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write 4884 // 4885 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) && 4886 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD))) 4887 { 4888 DPRINT1("Invalid page protection for rotate VAD\n"); 4889 Status = STATUS_INVALID_PAGE_PROTECTION; 4890 goto FailPath; 4891 } 4892 4893 // 4894 // Compute PTE addresses and the quota charge, then grab the commit lock 4895 // 4896 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT); 4897 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT); 4898 QuotaCharge = (ULONG)(LastPte - PointerPte + 1); 4899 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); 4900 4901 // 4902 // Get the segment template PTE and start looping each page 4903 // 4904 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate; 4905 ASSERT(TempPte.u.Long != 0); 4906 while (PointerPte <= LastPte) 4907 { 4908 // 4909 // For each non-already-committed page, write the invalid template PTE 4910 // 4911 if (PointerPte->u.Long == 0) 4912 { 4913 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 4914 } 4915 else 4916 { 4917 QuotaFree++; 4918 } 4919 PointerPte++; 4920 } 4921 4922 // 4923 // Now do the commit accounting and release the lock 4924 // 4925 ASSERT(QuotaCharge >= QuotaFree); 4926 QuotaCharge -= QuotaFree; 4927 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge; 4928 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 4929 4930 // 4931 // We are done with committing the section pages 4932 // 4933 Status = STATUS_SUCCESS; 4934 goto FailPath; 4935 } 4936 4937 // 4938 // This is a specific ReactOS check because we only use normal VADs 4939 // 4940 ASSERT(FoundVad->u.VadFlags.VadType == VadNone); 4941 4942 // 4943 // While this is an actual Windows check 4944 // 4945 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical); 4946 4947 // 4948 // Throw out attempts to use copy-on-write through this API path 4949 // 4950 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)) 4951 { 4952 DPRINT1("Write copy attempted when not allowed\n"); 4953 Status = STATUS_INVALID_PAGE_PROTECTION; 4954 goto FailPath; 4955 } 4956 4957 // 4958 // Initialize a demand-zero PTE 4959 // 4960 TempPte.u.Long = 0; 4961 TempPte.u.Soft.Protection = ProtectionMask; 4962 ASSERT(TempPte.u.Long != 0); 4963 4964 // 4965 // Get the PTE, PDE and the last PTE for this address range 4966 // 4967 PointerPde = MiAddressToPde(StartingAddress); 4968 PointerPte = MiAddressToPte(StartingAddress); 4969 LastPte = MiAddressToPte(EndingAddress); 4970 4971 // 4972 // Update the commit charge in the VAD as well as in the process, and check 4973 // if this commit charge was now higher than the last recorded peak, in which 4974 // case we also update the peak 4975 // 4976 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte); 4977 Process->CommitCharge += (1 + LastPte - PointerPte); 4978 if (Process->CommitCharge > Process->CommitChargePeak) 4979 { 4980 Process->CommitChargePeak = Process->CommitCharge; 4981 } 4982 4983 // 4984 // Lock the working set while we play with user pages and page tables 4985 // 4986 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 4987 4988 // 4989 // Make the current page table valid, and then loop each page within it 4990 // 4991 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 4992 while (PointerPte <= LastPte) 4993 { 4994 // 4995 // Have we crossed into a new page table? 4996 // 4997 if (MiIsPteOnPdeBoundary(PointerPte)) 4998 { 4999 // 5000 // Get the PDE and now make it valid too 5001 // 5002 PointerPde = MiPteToPde(PointerPte); 5003 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 5004 } 5005 5006 // 5007 // Is this a zero PTE as expected? 5008 // 5009 if (PointerPte->u.Long == 0) 5010 { 5011 // 5012 // First increment the count of pages in the page table for this 5013 // process 5014 // 5015 MiIncrementPageTableReferences(MiPteToAddress(PointerPte)); 5016 5017 // 5018 // And now write the invalid demand-zero PTE as requested 5019 // 5020 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5021 } 5022 else if (PointerPte->u.Long == MmDecommittedPte.u.Long) 5023 { 5024 // 5025 // If the PTE was already decommitted, there is nothing else to do 5026 // but to write the new demand-zero PTE 5027 // 5028 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 5029 } 5030 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte))) 5031 { 5032 // 5033 // We don't handle these scenarios yet 5034 // 5035 if (PointerPte->u.Soft.Valid == 0) 5036 { 5037 ASSERT(PointerPte->u.Soft.Prototype == 0); 5038 ASSERT(PointerPte->u.Soft.PageFileHigh == 0); 5039 } 5040 5041 // 5042 // There's a change in protection, remember this for later, but do 5043 // not yet handle it. 5044 // 5045 ChangeProtection = TRUE; 5046 } 5047 5048 // 5049 // Move to the next PTE 5050 // 5051 PointerPte++; 5052 } 5053 5054 // 5055 // Release the working set lock, unlock the address space, and detach from 5056 // the target process if it was not the current process. Also dereference the 5057 // target process if this wasn't the case. 5058 // 5059 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 5060 Status = STATUS_SUCCESS; 5061 FailPath: 5062 MmUnlockAddressSpace(AddressSpace); 5063 5064 if (!NT_SUCCESS(Status)) 5065 { 5066 if (Vad != NULL) 5067 { 5068 ExFreePoolWithTag(Vad, 'SdaV'); 5069 } 5070 } 5071 5072 // 5073 // Check if we need to update the protection 5074 // 5075 if (ChangeProtection) 5076 { 5077 PVOID ProtectBaseAddress = (PVOID)StartingAddress; 5078 SIZE_T ProtectSize = PRegionSize; 5079 ULONG OldProtection; 5080 5081 // 5082 // Change the protection of the region 5083 // 5084 MiProtectVirtualMemory(Process, 5085 &ProtectBaseAddress, 5086 &ProtectSize, 5087 Protect, 5088 &OldProtection); 5089 } 5090 5091 FailPathNoLock: 5092 if (Attached) KeUnstackDetachProcess(&ApcState); 5093 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5094 5095 // 5096 // Only write back results on success 5097 // 5098 if (NT_SUCCESS(Status)) 5099 { 5100 // 5101 // Use SEH to write back the base address and the region size. In the case 5102 // of an exception, we strangely do return back the exception code, even 5103 // though the memory *has* been allocated. This mimics Windows behavior and 5104 // there is not much we can do about it. 5105 // 5106 _SEH2_TRY 5107 { 5108 *URegionSize = PRegionSize; 5109 *UBaseAddress = (PVOID)StartingAddress; 5110 } 5111 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5112 { 5113 Status = _SEH2_GetExceptionCode(); 5114 } 5115 _SEH2_END; 5116 } 5117 5118 return Status; 5119 } 5120 5121 /* 5122 * @implemented 5123 */ 5124 NTSTATUS 5125 NTAPI 5126 NtFreeVirtualMemory(IN HANDLE ProcessHandle, 5127 IN PVOID* UBaseAddress, 5128 IN PSIZE_T URegionSize, 5129 IN ULONG FreeType) 5130 { 5131 PMEMORY_AREA MemoryArea; 5132 SIZE_T PRegionSize; 5133 PVOID PBaseAddress; 5134 LONG_PTR AlreadyDecommitted, CommitReduction = 0; 5135 ULONG_PTR StartingAddress, EndingAddress; 5136 PMMVAD Vad; 5137 NTSTATUS Status; 5138 PEPROCESS Process; 5139 PMMSUPPORT AddressSpace; 5140 PETHREAD CurrentThread = PsGetCurrentThread(); 5141 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 5142 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 5143 KAPC_STATE ApcState; 5144 BOOLEAN Attached = FALSE; 5145 PAGED_CODE(); 5146 5147 // 5148 // Only two flags are supported, exclusively. 5149 // 5150 if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT) 5151 { 5152 DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType); 5153 return STATUS_INVALID_PARAMETER_4; 5154 } 5155 5156 // 5157 // Enter SEH for probe and capture. On failure, return back to the caller 5158 // with an exception violation. 5159 // 5160 _SEH2_TRY 5161 { 5162 // 5163 // Check for user-mode parameters and make sure that they are writeable 5164 // 5165 if (PreviousMode != KernelMode) 5166 { 5167 ProbeForWritePointer(UBaseAddress); 5168 ProbeForWriteUlong(URegionSize); 5169 } 5170 5171 // 5172 // Capture the current values 5173 // 5174 PBaseAddress = *UBaseAddress; 5175 PRegionSize = *URegionSize; 5176 } 5177 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5178 { 5179 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 5180 } 5181 _SEH2_END; 5182 5183 // 5184 // Make sure the allocation isn't past the user area 5185 // 5186 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS) 5187 { 5188 DPRINT1("Virtual free base above User Space\n"); 5189 return STATUS_INVALID_PARAMETER_2; 5190 } 5191 5192 // 5193 // Make sure the allocation wouldn't overflow past the user area 5194 // 5195 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize) 5196 { 5197 DPRINT1("Region size would overflow into kernel-memory\n"); 5198 return STATUS_INVALID_PARAMETER_3; 5199 } 5200 5201 // 5202 // If this is for the current process, just use PsGetCurrentProcess 5203 // 5204 if (ProcessHandle == NtCurrentProcess()) 5205 { 5206 Process = CurrentProcess; 5207 } 5208 else 5209 { 5210 // 5211 // Otherwise, reference the process with VM rights and attach to it if 5212 // this isn't the current process. We must attach because we'll be touching 5213 // PTEs and PDEs that belong to user-mode memory, and also touching the 5214 // Working Set which is stored in Hyperspace. 5215 // 5216 Status = ObReferenceObjectByHandle(ProcessHandle, 5217 PROCESS_VM_OPERATION, 5218 PsProcessType, 5219 PreviousMode, 5220 (PVOID*)&Process, 5221 NULL); 5222 if (!NT_SUCCESS(Status)) return Status; 5223 if (CurrentProcess != Process) 5224 { 5225 KeStackAttachProcess(&Process->Pcb, &ApcState); 5226 Attached = TRUE; 5227 } 5228 } 5229 5230 DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n", 5231 Process, PBaseAddress, PRegionSize, FreeType); 5232 5233 // 5234 // Lock the address space 5235 // 5236 AddressSpace = MmGetCurrentAddressSpace(); 5237 MmLockAddressSpace(AddressSpace); 5238 5239 // 5240 // If the address space is being deleted, fail the de-allocation since it's 5241 // too late to do anything about it 5242 // 5243 if (Process->VmDeleted) 5244 { 5245 DPRINT1("Process is dead\n"); 5246 Status = STATUS_PROCESS_IS_TERMINATING; 5247 goto FailPath; 5248 } 5249 5250 // 5251 // Compute start and end addresses, and locate the VAD 5252 // 5253 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 5254 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 5255 Vad = MiLocateAddress((PVOID)StartingAddress); 5256 if (!Vad) 5257 { 5258 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress); 5259 Status = STATUS_MEMORY_NOT_ALLOCATED; 5260 goto FailPath; 5261 } 5262 5263 // 5264 // If the range exceeds the VAD's ending VPN, fail this request 5265 // 5266 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT)) 5267 { 5268 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress); 5269 Status = STATUS_UNABLE_TO_FREE_VM; 5270 goto FailPath; 5271 } 5272 5273 // 5274 // Only private memory (except rotate VADs) can be freed through here */ 5275 // 5276 if ((!(Vad->u.VadFlags.PrivateMemory) && 5277 (Vad->u.VadFlags.VadType != VadRotatePhysical)) || 5278 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory)) 5279 { 5280 DPRINT1("Attempt to free section memory\n"); 5281 Status = STATUS_UNABLE_TO_DELETE_SECTION; 5282 goto FailPath; 5283 } 5284 5285 // 5286 // ARM3 does not yet handle protected VM 5287 // 5288 ASSERT(Vad->u.VadFlags.NoChange == 0); 5289 5290 // 5291 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation 5292 // and that is is an ARM3 memory area, and not a section view, as we currently 5293 // don't support freeing those though this interface. 5294 // 5295 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress); 5296 ASSERT(MemoryArea); 5297 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3); 5298 5299 // 5300 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT 5301 // 5302 if (FreeType & MEM_RELEASE) 5303 { 5304 // 5305 // ARM3 only supports this VAD in this path 5306 // 5307 ASSERT(Vad->u.VadFlags.VadType == VadNone); 5308 5309 // 5310 // Is the caller trying to remove the whole VAD, or remove only a portion 5311 // of it? If no region size is specified, then the assumption is that the 5312 // whole VAD is to be destroyed 5313 // 5314 if (!PRegionSize) 5315 { 5316 // 5317 // The caller must specify the base address identically to the range 5318 // that is stored in the VAD. 5319 // 5320 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 5321 { 5322 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress); 5323 Status = STATUS_FREE_VM_NOT_AT_BASE; 5324 goto FailPath; 5325 } 5326 5327 // 5328 // Now compute the actual start/end addresses based on the VAD 5329 // 5330 StartingAddress = Vad->StartingVpn << PAGE_SHIFT; 5331 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 5332 5333 // 5334 // Finally lock the working set and remove the VAD from the VAD tree 5335 // 5336 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5337 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 5338 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 5339 } 5340 else 5341 { 5342 // 5343 // This means the caller wants to release a specific region within 5344 // the range. We have to find out which range this is -- the following 5345 // possibilities exist plus their union (CASE D): 5346 // 5347 // STARTING ADDRESS ENDING ADDRESS 5348 // [<========][========================================][=========>] 5349 // CASE A CASE B CASE C 5350 // 5351 // 5352 // First, check for case A or D 5353 // 5354 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn) 5355 { 5356 // 5357 // Check for case D 5358 // 5359 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 5360 { 5361 // 5362 // This is the easiest one to handle -- it is identical to 5363 // the code path above when the caller sets a zero region size 5364 // and the whole VAD is destroyed 5365 // 5366 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5367 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 5368 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 5369 } 5370 else 5371 { 5372 // 5373 // This case is pretty easy too -- we compute a bunch of 5374 // pages to decommit, and then push the VAD's starting address 5375 // a bit further down, then decrement the commit charge 5376 // 5377 // NOT YET IMPLEMENTED IN ARM3. 5378 // 5379 DPRINT1("Case A not handled\n"); 5380 Status = STATUS_FREE_VM_NOT_AT_BASE; 5381 goto FailPath; 5382 5383 // 5384 // After analyzing the VAD, set it to NULL so that we don't 5385 // free it in the exit path 5386 // 5387 Vad = NULL; 5388 } 5389 } 5390 else 5391 { 5392 // 5393 // This is case B or case C. First check for case C 5394 // 5395 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 5396 { 5397 PMEMORY_AREA MemoryArea; 5398 5399 // 5400 // This is pretty easy and similar to case A. We compute the 5401 // amount of pages to decommit, update the VAD's commit charge 5402 // and then change the ending address of the VAD to be a bit 5403 // smaller. 5404 // 5405 MiLockProcessWorkingSetUnsafe(Process, CurrentThread); 5406 CommitReduction = MiCalculatePageCommitment(StartingAddress, 5407 EndingAddress, 5408 Vad, 5409 Process); 5410 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5411 // For ReactOS: shrink the corresponding memory area 5412 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress); 5413 ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn); 5414 ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn); 5415 Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT; 5416 MemoryArea->VadNode.EndingVpn = Vad->EndingVpn; 5417 } 5418 else 5419 { 5420 // 5421 // This is case B and the hardest one. Because we are removing 5422 // a chunk of memory from the very middle of the VAD, we must 5423 // actually split the VAD into two new VADs and compute the 5424 // commit charges for each of them, and reinsert new charges. 5425 // 5426 // NOT YET IMPLEMENTED IN ARM3. 5427 // 5428 DPRINT1("Case B not handled\n"); 5429 Status = STATUS_FREE_VM_NOT_AT_BASE; 5430 goto FailPath; 5431 } 5432 5433 // 5434 // After analyzing the VAD, set it to NULL so that we don't 5435 // free it in the exit path 5436 // 5437 Vad = NULL; 5438 } 5439 } 5440 5441 // 5442 // Now we have a range of pages to dereference, so call the right API 5443 // to do that and then release the working set, since we're done messing 5444 // around with process pages. 5445 // 5446 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL); 5447 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread); 5448 Status = STATUS_SUCCESS; 5449 5450 FinalPath: 5451 // 5452 // Update the process counters 5453 // 5454 PRegionSize = EndingAddress - StartingAddress + 1; 5455 Process->CommitCharge -= CommitReduction; 5456 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize; 5457 5458 // 5459 // Unlock the address space and free the VAD in failure cases. Next, 5460 // detach from the target process so we can write the region size and the 5461 // base address to the correct source process, and dereference the target 5462 // process. 5463 // 5464 MmUnlockAddressSpace(AddressSpace); 5465 if (Vad) ExFreePool(Vad); 5466 if (Attached) KeUnstackDetachProcess(&ApcState); 5467 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5468 5469 // 5470 // Use SEH to safely return the region size and the base address of the 5471 // deallocation. If we get an access violation, don't return a failure code 5472 // as the deallocation *has* happened. The caller will just have to figure 5473 // out another way to find out where it is (such as VirtualQuery). 5474 // 5475 _SEH2_TRY 5476 { 5477 *URegionSize = PRegionSize; 5478 *UBaseAddress = (PVOID)StartingAddress; 5479 } 5480 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 5481 { 5482 } 5483 _SEH2_END; 5484 return Status; 5485 } 5486 5487 // 5488 // This is the decommit path. You cannot decommit from the following VADs in 5489 // Windows, so fail the vall 5490 // 5491 if ((Vad->u.VadFlags.VadType == VadAwe) || 5492 (Vad->u.VadFlags.VadType == VadLargePages) || 5493 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 5494 { 5495 DPRINT1("Trying to decommit from invalid VAD\n"); 5496 Status = STATUS_MEMORY_NOT_ALLOCATED; 5497 goto FailPath; 5498 } 5499 5500 // 5501 // If the caller did not specify a region size, first make sure that this 5502 // region is actually committed. If it is, then compute the ending address 5503 // based on the VAD. 5504 // 5505 if (!PRegionSize) 5506 { 5507 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 5508 { 5509 DPRINT1("Decomitting non-committed memory\n"); 5510 Status = STATUS_FREE_VM_NOT_AT_BASE; 5511 goto FailPath; 5512 } 5513 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 5514 } 5515 5516 // 5517 // Decommit the PTEs for the range plus the actual backing pages for the 5518 // range, then reduce that amount from the commit charge in the VAD 5519 // 5520 AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress, 5521 MiAddressToPte(EndingAddress), 5522 Process, 5523 Vad); 5524 CommitReduction = MiAddressToPte(EndingAddress) - 5525 MiAddressToPte(StartingAddress) + 5526 1 - 5527 AlreadyDecommitted; 5528 5529 ASSERT(CommitReduction >= 0); 5530 ASSERT(Vad->u.VadFlags.CommitCharge >= CommitReduction); 5531 Vad->u.VadFlags.CommitCharge -= CommitReduction; 5532 5533 // 5534 // We are done, go to the exit path without freeing the VAD as it remains 5535 // valid since we have not released the allocation. 5536 // 5537 Vad = NULL; 5538 Status = STATUS_SUCCESS; 5539 goto FinalPath; 5540 5541 // 5542 // In the failure path, we detach and derefernece the target process, and 5543 // return whatever failure code was sent. 5544 // 5545 FailPath: 5546 MmUnlockAddressSpace(AddressSpace); 5547 if (Attached) KeUnstackDetachProcess(&ApcState); 5548 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 5549 return Status; 5550 } 5551 5552 5553 PHYSICAL_ADDRESS 5554 NTAPI 5555 MmGetPhysicalAddress(PVOID Address) 5556 { 5557 PHYSICAL_ADDRESS PhysicalAddress; 5558 MMPDE TempPde; 5559 MMPTE TempPte; 5560 5561 /* Check if the PXE/PPE/PDE is valid */ 5562 if ( 5563 #if (_MI_PAGING_LEVELS == 4) 5564 (MiAddressToPxe(Address)->u.Hard.Valid) && 5565 #endif 5566 #if (_MI_PAGING_LEVELS >= 3) 5567 (MiAddressToPpe(Address)->u.Hard.Valid) && 5568 #endif 5569 (MiAddressToPde(Address)->u.Hard.Valid)) 5570 { 5571 /* Check for large pages */ 5572 TempPde = *MiAddressToPde(Address); 5573 if (TempPde.u.Hard.LargePage) 5574 { 5575 /* Physical address is base page + large page offset */ 5576 PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT; 5577 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1)); 5578 return PhysicalAddress; 5579 } 5580 5581 /* Check if the PTE is valid */ 5582 TempPte = *MiAddressToPte(Address); 5583 if (TempPte.u.Hard.Valid) 5584 { 5585 /* Physical address is base page + page offset */ 5586 PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT; 5587 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1)); 5588 return PhysicalAddress; 5589 } 5590 } 5591 5592 KeRosDumpStackFrames(NULL, 20); 5593 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address); 5594 PhysicalAddress.QuadPart = 0; 5595 return PhysicalAddress; 5596 } 5597 5598 5599 /* EOF */ 5600