1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/mm/i386/page.c 5 * PURPOSE: Low level memory management manipulation 6 * 7 * PROGRAMMERS: David Welch (welch@cwcom.net) 8 */ 9 10 /* INCLUDES ***************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 #include <mm/ARM3/miarm.h> 17 18 #ifndef _MI_PAGING_LEVELS 19 #error "Dude, fix your stuff before using this file" 20 #endif 21 22 /* GLOBALS *****************************************************************/ 23 const 24 ULONG_PTR 25 MmProtectToPteMask[32] = 26 { 27 // 28 // These are the base MM_ protection flags 29 // 30 0, 31 PTE_READONLY | PTE_ENABLE_CACHE, 32 PTE_EXECUTE | PTE_ENABLE_CACHE, 33 PTE_EXECUTE_READ | PTE_ENABLE_CACHE, 34 PTE_READWRITE | PTE_ENABLE_CACHE, 35 PTE_WRITECOPY | PTE_ENABLE_CACHE, 36 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE, 37 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE, 38 // 39 // These OR in the MM_NOCACHE flag 40 // 41 0, 42 PTE_READONLY | PTE_DISABLE_CACHE, 43 PTE_EXECUTE | PTE_DISABLE_CACHE, 44 PTE_EXECUTE_READ | PTE_DISABLE_CACHE, 45 PTE_READWRITE | PTE_DISABLE_CACHE, 46 PTE_WRITECOPY | PTE_DISABLE_CACHE, 47 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE, 48 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE, 49 // 50 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM 51 // 52 0, 53 PTE_READONLY | PTE_ENABLE_CACHE, 54 PTE_EXECUTE | PTE_ENABLE_CACHE, 55 PTE_EXECUTE_READ | PTE_ENABLE_CACHE, 56 PTE_READWRITE | PTE_ENABLE_CACHE, 57 PTE_WRITECOPY | PTE_ENABLE_CACHE, 58 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE, 59 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE, 60 // 61 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining? 62 // 63 0, 64 PTE_READONLY | PTE_WRITECOMBINED_CACHE, 65 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE, 66 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE, 67 PTE_READWRITE | PTE_WRITECOMBINED_CACHE, 68 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE, 69 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE, 70 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE, 71 }; 72 73 const 74 ULONG MmProtectToValue[32] = 75 { 76 PAGE_NOACCESS, 77 PAGE_READONLY, 78 PAGE_EXECUTE, 79 PAGE_EXECUTE_READ, 80 PAGE_READWRITE, 81 PAGE_WRITECOPY, 82 PAGE_EXECUTE_READWRITE, 83 PAGE_EXECUTE_WRITECOPY, 84 PAGE_NOACCESS, 85 PAGE_NOCACHE | PAGE_READONLY, 86 PAGE_NOCACHE | PAGE_EXECUTE, 87 PAGE_NOCACHE | PAGE_EXECUTE_READ, 88 PAGE_NOCACHE | PAGE_READWRITE, 89 PAGE_NOCACHE | PAGE_WRITECOPY, 90 PAGE_NOCACHE | PAGE_EXECUTE_READWRITE, 91 PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY, 92 PAGE_NOACCESS, 93 PAGE_GUARD | PAGE_READONLY, 94 PAGE_GUARD | PAGE_EXECUTE, 95 PAGE_GUARD | PAGE_EXECUTE_READ, 96 PAGE_GUARD | PAGE_READWRITE, 97 PAGE_GUARD | PAGE_WRITECOPY, 98 PAGE_GUARD | PAGE_EXECUTE_READWRITE, 99 PAGE_GUARD | PAGE_EXECUTE_WRITECOPY, 100 PAGE_NOACCESS, 101 PAGE_WRITECOMBINE | PAGE_READONLY, 102 PAGE_WRITECOMBINE | PAGE_EXECUTE, 103 PAGE_WRITECOMBINE | PAGE_EXECUTE_READ, 104 PAGE_WRITECOMBINE | PAGE_READWRITE, 105 PAGE_WRITECOMBINE | PAGE_WRITECOPY, 106 PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE, 107 PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY 108 }; 109 110 /* FUNCTIONS ***************************************************************/ 111 112 NTSTATUS 113 NTAPI 114 MiFillSystemPageDirectory(IN PVOID Base, 115 IN SIZE_T NumberOfBytes); 116 117 static 118 BOOLEAN 119 MiIsPageTablePresent(PVOID Address) 120 { 121 #if _MI_PAGING_LEVELS == 2 122 BOOLEAN Ret = MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] != 0; 123 124 /* Some sanity check while we're here */ 125 ASSERT(Ret == (MiAddressToPde(Address)->u.Hard.Valid != 0)); 126 127 return Ret; 128 #else 129 PMMPDE PointerPde; 130 PMMPPE PointerPpe; 131 #if _MI_PAGING_LEVELS == 4 132 PMMPXE PointerPxe; 133 #endif 134 PMMPFN Pfn; 135 136 /* Make sure we're locked */ 137 ASSERT((PsGetCurrentThread()->OwnsProcessWorkingSetExclusive) || (PsGetCurrentThread()->OwnsProcessWorkingSetShared)); 138 139 /* Must not hold the PFN lock! */ 140 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 141 142 /* Check if PXE or PPE have references first. */ 143 #if _MI_PAGING_LEVELS == 4 144 PointerPxe = MiAddressToPxe(Address); 145 if ((PointerPxe->u.Hard.Valid == 1) || (PointerPxe->u.Soft.Transition == 1)) 146 { 147 Pfn = MiGetPfnEntry(PFN_FROM_PXE(PointerPxe)); 148 if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0) 149 return FALSE; 150 } 151 else if (PointerPxe->u.Soft.UsedPageTableEntries == 0) 152 { 153 return FALSE; 154 } 155 156 if (PointerPxe->u.Hard.Valid == 0) 157 { 158 MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), PsGetCurrentProcess()); 159 } 160 #endif 161 162 PointerPpe = MiAddressToPpe(Address); 163 if ((PointerPpe->u.Hard.Valid == 1) || (PointerPpe->u.Soft.Transition == 1)) 164 { 165 Pfn = MiGetPfnEntry(PFN_FROM_PPE(PointerPpe)); 166 if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0) 167 return FALSE; 168 } 169 else if (PointerPpe->u.Soft.UsedPageTableEntries == 0) 170 { 171 return FALSE; 172 } 173 174 if (PointerPpe->u.Hard.Valid == 0) 175 { 176 MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), PsGetCurrentProcess()); 177 } 178 179 PointerPde = MiAddressToPde(Address); 180 if ((PointerPde->u.Hard.Valid == 0) && (PointerPde->u.Soft.Transition == 0)) 181 { 182 return PointerPde->u.Soft.UsedPageTableEntries != 0; 183 } 184 185 /* This lies on the PFN */ 186 Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde)); 187 return Pfn->OriginalPte.u.Soft.UsedPageTableEntries != 0; 188 #endif 189 } 190 191 PFN_NUMBER 192 NTAPI 193 MmGetPfnForProcess(PEPROCESS Process, 194 PVOID Address) 195 { 196 PMMPTE PointerPte; 197 PFN_NUMBER Page; 198 199 /* Must be called for user mode only */ 200 ASSERT(Process != NULL); 201 ASSERT(Address < MmSystemRangeStart); 202 203 /* And for our process */ 204 ASSERT(Process == PsGetCurrentProcess()); 205 206 /* Lock for reading */ 207 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 208 209 if (!MiIsPageTablePresent(Address)) 210 { 211 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 212 return 0; 213 } 214 215 /* Make sure we can read the PTE */ 216 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 217 218 PointerPte = MiAddressToPte(Address); 219 Page = PointerPte->u.Hard.Valid ? PFN_FROM_PTE(PointerPte) : 0; 220 221 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 222 return Page; 223 } 224 225 /** 226 * @brief Deletes the virtual mapping and optionally gives back the page & dirty bit. 227 * 228 * @param Process - The process this address belongs to, or NULL if system address. 229 * @param Address - The address to unmap. 230 * @param WasDirty - Optional param receiving the dirty bit of the PTE. 231 * @param Page - Optional param receiving the page number previously mapped to this address. 232 * 233 * @return Whether there was actually a page mapped at the given address. 234 */ 235 _Success_(return) 236 BOOLEAN 237 MmDeleteVirtualMappingEx( 238 _Inout_opt_ PEPROCESS Process, 239 _In_ PVOID Address, 240 _Out_opt_ BOOLEAN* WasDirty, 241 _Out_opt_ PPFN_NUMBER Page, 242 _In_ BOOLEAN IsPhysical) 243 { 244 PMMPTE PointerPte; 245 MMPTE OldPte; 246 BOOLEAN ValidPde; 247 248 OldPte.u.Long = 0; 249 250 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page); 251 252 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0); 253 254 /* And we should be at low IRQL */ 255 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 256 257 /* Make sure our PDE is valid, and that everything is going fine */ 258 if (Process == NULL) 259 { 260 if (Address < MmSystemRangeStart) 261 { 262 DPRINT1("NULL process given for user-mode mapping at %p\n", Address); 263 KeBugCheck(MEMORY_MANAGEMENT); 264 } 265 #if (_MI_PAGING_LEVELS == 2) 266 ValidPde = MiSynchronizeSystemPde(MiAddressToPde(Address)); 267 #else 268 ValidPde = MiIsPdeForAddressValid(Address); 269 #endif 270 } 271 else 272 { 273 if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart) 274 { 275 DPRINT1("Process %p given for kernel-mode mapping at %p\n", Process, Address); 276 KeBugCheck(MEMORY_MANAGEMENT); 277 } 278 279 /* Only for current process !!! */ 280 ASSERT(Process == PsGetCurrentProcess()); 281 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 282 283 ValidPde = MiIsPageTablePresent(Address); 284 if (ValidPde) 285 { 286 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 287 } 288 } 289 290 /* Get the PTE if we're having anything */ 291 if (ValidPde) 292 { 293 PointerPte = MiAddressToPte(Address); 294 OldPte.u.Long = InterlockedExchangePte(PointerPte, 0); 295 296 KeInvalidateTlbEntry(Address); 297 298 if (OldPte.u.Long != 0) 299 { 300 /* It must have been present, or not a swap entry */ 301 ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800)); 302 if (WasDirty != NULL) 303 { 304 *WasDirty = !!OldPte.u.Hard.Dirty; 305 } 306 if (Page != NULL) 307 { 308 *Page = OldPte.u.Hard.PageFrameNumber; 309 } 310 } 311 } 312 313 if (Process != NULL) 314 { 315 /* Remove PDE reference, if needed */ 316 if (OldPte.u.Long != 0) 317 { 318 if (MiDecrementPageTableReferences(Address) == 0) 319 { 320 KIRQL OldIrql = MiAcquirePfnLock(); 321 MiDeletePde(MiAddressToPde(Address), Process); 322 MiReleasePfnLock(OldIrql); 323 } 324 } 325 326 if (!IsPhysical && OldPte.u.Hard.Valid) 327 { 328 PMMPFN Pfn1; 329 KIRQL OldIrql; 330 331 OldIrql = MiAcquirePfnLock(); 332 Pfn1 = &MmPfnDatabase[OldPte.u.Hard.PageFrameNumber]; 333 ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid); 334 ASSERT(Pfn1->u2.ShareCount > 0); 335 if (--Pfn1->u2.ShareCount == 0) 336 { 337 Pfn1->u3.e1.PageLocation = TransitionPage; 338 } 339 MiReleasePfnLock(OldIrql); 340 } 341 342 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 343 } 344 345 return OldPte.u.Long != 0; 346 } 347 348 _Success_(return) 349 BOOLEAN 350 MmDeleteVirtualMapping( 351 _Inout_opt_ PEPROCESS Process, 352 _In_ PVOID Address, 353 _Out_opt_ BOOLEAN * WasDirty, 354 _Out_opt_ PPFN_NUMBER Page) 355 { 356 return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, FALSE); 357 } 358 359 _Success_(return) 360 BOOLEAN 361 MmDeletePhysicalMapping( 362 _Inout_opt_ PEPROCESS Process, 363 _In_ PVOID Address, 364 _Out_opt_ BOOLEAN * WasDirty, 365 _Out_opt_ PPFN_NUMBER Page) 366 { 367 return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, TRUE); 368 } 369 370 VOID 371 NTAPI 372 MmDeletePageFileMapping( 373 PEPROCESS Process, 374 PVOID Address, 375 SWAPENTRY* SwapEntry) 376 { 377 PMMPTE PointerPte; 378 MMPTE OldPte; 379 380 /* This should not be called for kernel space anymore */ 381 ASSERT(Process != NULL); 382 ASSERT(Address < MmSystemRangeStart); 383 384 /* And we don't support deleting for other process */ 385 ASSERT(Process == PsGetCurrentProcess()); 386 387 /* And we should be at low IRQL */ 388 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 389 390 /* We are tinkering with the PDE here. Ensure it will be there */ 391 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 392 393 /* Callers must ensure there is actually something there */ 394 ASSERT(MiAddressToPde(Address)->u.Long != 0); 395 396 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 397 398 PointerPte = MiAddressToPte(Address); 399 OldPte.u.Long = InterlockedExchangePte(PointerPte, 0); 400 /* This must be a swap entry ! */ 401 if (!FlagOn(OldPte.u.Long, 0x800) || OldPte.u.Hard.Valid) 402 { 403 KeBugCheckEx(MEMORY_MANAGEMENT, OldPte.u.Long, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); 404 } 405 406 /* This used to be a non-zero PTE, now we can let the PDE go. */ 407 if (MiDecrementPageTableReferences(Address) == 0) 408 { 409 /* We can let it go */ 410 KIRQL OldIrql = MiAcquirePfnLock(); 411 MiDeletePde(MiPteToPde(PointerPte), Process); 412 MiReleasePfnLock(OldIrql); 413 } 414 415 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 416 417 *SwapEntry = OldPte.u.Long >> 1; 418 } 419 420 BOOLEAN 421 NTAPI 422 MmIsPagePresent(PEPROCESS Process, PVOID Address) 423 { 424 BOOLEAN Ret; 425 426 if (Address >= MmSystemRangeStart) 427 { 428 ASSERT(Process == NULL); 429 #if _MI_PAGING_LEVELS == 2 430 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 431 #else 432 if (!MiIsPdeForAddressValid(Address)) 433 #endif 434 { 435 /* It can't be present if there is no PDE */ 436 return FALSE; 437 } 438 439 return MiAddressToPte(Address)->u.Hard.Valid; 440 } 441 442 ASSERT(Process != NULL); 443 ASSERT(Process == PsGetCurrentProcess()); 444 445 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 446 447 if (!MiIsPageTablePresent(Address)) 448 { 449 /* It can't be present if there is no PDE */ 450 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 451 return FALSE; 452 } 453 454 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 455 456 Ret = MiAddressToPte(Address)->u.Hard.Valid; 457 458 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 459 460 return Ret; 461 } 462 463 BOOLEAN 464 NTAPI 465 MmIsDisabledPage(PEPROCESS Process, PVOID Address) 466 { 467 BOOLEAN Ret; 468 PMMPTE PointerPte; 469 470 if (Address >= MmSystemRangeStart) 471 { 472 ASSERT(Process == NULL); 473 #if _MI_PAGING_LEVELS == 2 474 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 475 #else 476 if (!MiIsPdeForAddressValid(Address)) 477 #endif 478 { 479 /* It's not disabled if it's not present */ 480 return FALSE; 481 } 482 } 483 else 484 { 485 ASSERT(Process != NULL); 486 ASSERT(Process == PsGetCurrentProcess()); 487 488 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 489 490 if (!MiIsPageTablePresent(Address)) 491 { 492 /* It can't be disabled if there is no PDE */ 493 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 494 return FALSE; 495 } 496 497 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 498 } 499 500 PointerPte = MiAddressToPte(Address); 501 Ret = !PointerPte->u.Hard.Valid 502 && !FlagOn(PointerPte->u.Long, 0x800) 503 && (PointerPte->u.Hard.PageFrameNumber != 0); 504 505 if (Address < MmSystemRangeStart) 506 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 507 508 return Ret; 509 } 510 511 BOOLEAN 512 NTAPI 513 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) 514 { 515 BOOLEAN Ret; 516 PMMPTE PointerPte; 517 518 /* We never set swap entries for kernel addresses */ 519 if (Address >= MmSystemRangeStart) 520 { 521 ASSERT(Process == NULL); 522 return FALSE; 523 } 524 525 ASSERT(Process != NULL); 526 ASSERT(Process == PsGetCurrentProcess()); 527 528 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 529 530 if (!MiIsPageTablePresent(Address)) 531 { 532 /* There can't be a swap entry if there is no PDE */ 533 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 534 return FALSE; 535 } 536 537 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 538 539 PointerPte = MiAddressToPte(Address); 540 Ret = !PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800); 541 542 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 543 544 return Ret; 545 } 546 547 VOID 548 NTAPI 549 MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry) 550 { 551 PMMPTE PointerPte; 552 553 /* We never set swap entries for kernel addresses */ 554 if (Address >= MmSystemRangeStart) 555 { 556 ASSERT(Process == NULL); 557 *SwapEntry = 0; 558 return; 559 } 560 561 ASSERT(Process != NULL); 562 ASSERT(Process == PsGetCurrentProcess()); 563 564 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 565 566 if (!MiIsPageTablePresent(Address)) 567 { 568 /* There can't be a swap entry if there is no PDE */ 569 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 570 *SwapEntry = 0; 571 return; 572 } 573 574 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 575 576 PointerPte = MiAddressToPte(Address); 577 if (!PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800)) 578 *SwapEntry = PointerPte->u.Long >> 1; 579 else 580 *SwapEntry = 0; 581 582 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 583 } 584 585 NTSTATUS 586 NTAPI 587 MmCreatePageFileMapping(PEPROCESS Process, 588 PVOID Address, 589 SWAPENTRY SwapEntry) 590 { 591 PMMPTE PointerPte; 592 ULONG_PTR Pte; 593 594 /* This should not be called for kernel space anymore */ 595 ASSERT(Process != NULL); 596 ASSERT(Address < MmSystemRangeStart); 597 598 /* And we don't support creating for other process */ 599 ASSERT(Process == PsGetCurrentProcess()); 600 601 if (SwapEntry & ((ULONG_PTR)1 << (RTL_BITS_OF(SWAPENTRY) - 1))) 602 { 603 KeBugCheck(MEMORY_MANAGEMENT); 604 } 605 606 /* We are tinkering with the PDE here. Ensure it will be there */ 607 ASSERT(Process == PsGetCurrentProcess()); 608 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 609 610 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 611 612 PointerPte = MiAddressToPte(Address); 613 Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1); 614 if (Pte != 0) 615 { 616 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); 617 } 618 619 /* This used to be a 0 PTE, now we need a valid PDE to keep it around */ 620 MiIncrementPageTableReferences(Address); 621 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 622 623 return STATUS_SUCCESS; 624 } 625 626 627 NTSTATUS 628 NTAPI 629 MmCreateVirtualMappingUnsafeEx( 630 _Inout_opt_ PEPROCESS Process, 631 _In_ PVOID Address, 632 _In_ ULONG flProtect, 633 _In_ PFN_NUMBER Page, 634 _In_ BOOLEAN IsPhysical) 635 { 636 ULONG ProtectionMask; 637 PMMPTE PointerPte; 638 MMPTE TempPte; 639 ULONG_PTR Pte; 640 641 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n", 642 Process, Address, flProtect, Page); 643 644 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0); 645 646 ProtectionMask = MiMakeProtectionMask(flProtect); 647 /* Caller must have checked ! */ 648 ASSERT(ProtectionMask != MM_INVALID_PROTECTION); 649 ASSERT(ProtectionMask != MM_NOACCESS); 650 ASSERT(ProtectionMask != MM_ZERO_ACCESS); 651 652 /* Make sure our PDE is valid, and that everything is going fine */ 653 if (Process == NULL) 654 { 655 /* We don't support this in legacy Mm for kernel mappings */ 656 ASSERT(ProtectionMask != MM_WRITECOPY); 657 ASSERT(ProtectionMask != MM_EXECUTE_WRITECOPY); 658 659 if (Address < MmSystemRangeStart) 660 { 661 DPRINT1("NULL process given for user-mode mapping at %p\n", Address); 662 KeBugCheck(MEMORY_MANAGEMENT); 663 } 664 #if _MI_PAGING_LEVELS == 2 665 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 666 MiFillSystemPageDirectory(Address, PAGE_SIZE); 667 #endif 668 } 669 else 670 { 671 if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart) 672 { 673 DPRINT1("Process %p given for kernel-mode mapping at %p -- 1 page starting at %Ix\n", 674 Process, Address, Page); 675 KeBugCheck(MEMORY_MANAGEMENT); 676 } 677 678 /* Only for current process !!! */ 679 ASSERT(Process == PsGetCurrentProcess()); 680 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 681 682 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 683 } 684 685 PointerPte = MiAddressToPte(Address); 686 687 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page); 688 689 Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long); 690 /* There should not have been anything valid here */ 691 if (Pte != 0) 692 { 693 DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address); 694 KeBugCheck(MEMORY_MANAGEMENT); 695 } 696 697 if (!IsPhysical) 698 { 699 PMMPFN Pfn1; 700 KIRQL OldIrql; 701 702 OldIrql = MiAcquirePfnLock(); 703 Pfn1 = &MmPfnDatabase[TempPte.u.Hard.PageFrameNumber]; 704 Pfn1->u2.ShareCount++; 705 Pfn1->u3.e1.PageLocation = ActiveAndValid; 706 MiReleasePfnLock(OldIrql); 707 } 708 709 /* We don't need to flush the TLB here because it only caches valid translations 710 * and we're moving this PTE from invalid to valid so it can't be cached right now */ 711 712 if (Address < MmSystemRangeStart) 713 { 714 /* Add PDE reference */ 715 MiIncrementPageTableReferences(Address); 716 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 717 } 718 719 return(STATUS_SUCCESS); 720 } 721 722 NTSTATUS 723 NTAPI 724 MmCreateVirtualMappingUnsafe( 725 _Inout_opt_ PEPROCESS Process, 726 _In_ PVOID Address, 727 _In_ ULONG flProtect, 728 _In_ PFN_NUMBER Page) 729 { 730 return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, FALSE); 731 } 732 733 NTSTATUS 734 NTAPI 735 MmCreatePhysicalMapping( 736 _Inout_opt_ PEPROCESS Process, 737 _In_ PVOID Address, 738 _In_ ULONG flProtect, 739 _In_ PFN_NUMBER Page) 740 { 741 return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, TRUE); 742 } 743 744 NTSTATUS 745 NTAPI 746 MmCreateVirtualMapping(PEPROCESS Process, 747 PVOID Address, 748 ULONG flProtect, 749 PFN_NUMBER Page) 750 { 751 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0); 752 if (!MmIsPageInUse(Page)) 753 { 754 DPRINT1("Page %lx is not in use\n", Page); 755 KeBugCheck(MEMORY_MANAGEMENT); 756 } 757 758 return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page); 759 } 760 761 ULONG 762 NTAPI 763 MmGetPageProtect(PEPROCESS Process, PVOID Address) 764 { 765 PMMPTE PointerPte; 766 ULONG Protect; 767 768 if (Address >= MmSystemRangeStart) 769 { 770 ASSERT(Process == NULL); 771 772 #if _MI_PAGING_LEVELS == 2 773 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 774 #else 775 if (!MiIsPdeForAddressValid(Address)) 776 #endif 777 { 778 return PAGE_NOACCESS; 779 } 780 } 781 else 782 { 783 ASSERT(Address < MmSystemRangeStart); 784 ASSERT(Process != NULL); 785 786 ASSERT(Process == PsGetCurrentProcess()); 787 788 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 789 790 if (!MiIsPageTablePresent(Address)) 791 { 792 /* It can't be present if there is no PDE */ 793 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 794 return PAGE_NOACCESS; 795 } 796 797 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 798 } 799 800 PointerPte = MiAddressToPte(Address); 801 802 if (!PointerPte->u.Flush.Valid) 803 { 804 Protect = PAGE_NOACCESS; 805 } 806 else 807 { 808 if (PointerPte->u.Flush.CopyOnWrite) 809 Protect = PAGE_WRITECOPY; 810 else if (PointerPte->u.Flush.Write) 811 Protect = PAGE_READWRITE; 812 else 813 Protect = PAGE_READONLY; 814 #if _MI_PAGING_LEVELS >= 3 815 /* PAE & AMD64 long mode support NoExecute bit */ 816 if (!PointerPte->u.Flush.NoExecute) 817 Protect <<= 4; 818 #endif 819 if (PointerPte->u.Flush.CacheDisable) 820 Protect |= PAGE_NOCACHE; 821 if (PointerPte->u.Flush.WriteThrough) 822 Protect |= PAGE_WRITETHROUGH; 823 } 824 825 if (Address < MmSystemRangeStart) 826 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 827 828 return(Protect); 829 } 830 831 VOID 832 NTAPI 833 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect) 834 { 835 ULONG ProtectionMask; 836 PMMPTE PointerPte; 837 MMPTE TempPte, OldPte; 838 839 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n", 840 Process, Address, flProtect); 841 842 ASSERT(Process != NULL); 843 ASSERT(Address < MmSystemRangeStart); 844 845 ASSERT(Process == PsGetCurrentProcess()); 846 847 ProtectionMask = MiMakeProtectionMask(flProtect); 848 /* Caller must have checked ! */ 849 ASSERT(ProtectionMask != MM_INVALID_PROTECTION); 850 851 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 852 853 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 854 855 PointerPte = MiAddressToPte(Address); 856 857 /* Sanity check */ 858 ASSERT(PointerPte->u.Hard.Owner == 1); 859 860 TempPte.u.Long = 0; 861 TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber; 862 TempPte.u.Long |= MmProtectToPteMask[ProtectionMask]; 863 TempPte.u.Hard.Owner = 1; 864 865 /* Only set valid bit if we have to */ 866 if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE)) 867 TempPte.u.Hard.Valid = 1; 868 869 /* Keep dirty & accessed bits */ 870 TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed; 871 TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty; 872 873 OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long); 874 875 // We should be able to bring a page back from PAGE_NOACCESS 876 if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0))) 877 { 878 DPRINT1("Invalid Pte %lx\n", OldPte.u.Long); 879 KeBugCheck(MEMORY_MANAGEMENT); 880 } 881 882 if (OldPte.u.Long != TempPte.u.Long) 883 KeInvalidateTlbEntry(Address); 884 885 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 886 } 887 888 VOID 889 NTAPI 890 MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit) 891 { 892 PMMPTE PointerPte; 893 894 DPRINT("MmSetDirtyBit(Process %p Address %p Bit %x)\n", 895 Process, Address, Bit); 896 897 ASSERT(Process != NULL); 898 ASSERT(Address < MmSystemRangeStart); 899 900 ASSERT(Process == PsGetCurrentProcess()); 901 902 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 903 904 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 905 906 PointerPte = MiAddressToPte(Address); 907 // We shouldnl't set dirty bit on non-mapped addresses 908 if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0))) 909 { 910 DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long); 911 KeBugCheck(MEMORY_MANAGEMENT); 912 } 913 914 PointerPte->u.Hard.Dirty = !!Bit; 915 916 if (!Bit) 917 KeInvalidateTlbEntry(Address); 918 919 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 920 } 921 922 CODE_SEG("INIT") 923 VOID 924 NTAPI 925 MmInitGlobalKernelPageDirectory(VOID) 926 { 927 /* Nothing to do here */ 928 } 929 930 #ifdef _M_IX86 931 BOOLEAN 932 Mmi386MakeKernelPageTableGlobal(PVOID Address) 933 { 934 PMMPDE PointerPde = MiAddressToPde(Address); 935 PMMPTE PointerPte = MiAddressToPte(Address); 936 937 if (PointerPde->u.Hard.Valid == 0) 938 { 939 if (!MiSynchronizeSystemPde(PointerPde)) 940 return FALSE; 941 return PointerPte->u.Hard.Valid != 0; 942 } 943 return FALSE; 944 } 945 #endif 946 947 /* EOF */ 948