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