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