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