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 managment 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 MmDeleteVirtualMapping( 238 _In_opt_ PEPROCESS Process, 239 _In_ PVOID Address, 240 _Out_opt_ BOOLEAN* WasDirty, 241 _Out_opt_ PPFN_NUMBER Page) 242 { 243 PMMPTE PointerPte; 244 MMPTE OldPte; 245 BOOLEAN ValidPde; 246 247 OldPte.u.Long = 0; 248 249 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page); 250 251 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0); 252 253 /* And we should be at low IRQL */ 254 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 255 256 /* Make sure our PDE is valid, and that everything is going fine */ 257 if (Process == NULL) 258 { 259 if (Address < MmSystemRangeStart) 260 { 261 DPRINT1("NULL process given for user-mode mapping at %p\n", Address); 262 KeBugCheck(MEMORY_MANAGEMENT); 263 } 264 #if (_MI_PAGING_LEVELS == 2) 265 ValidPde = MiSynchronizeSystemPde(MiAddressToPde(Address)); 266 #else 267 ValidPde = MiIsPdeForAddressValid(Address); 268 #endif 269 } 270 else 271 { 272 if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart) 273 { 274 DPRINT1("Process %p given for kernel-mode mapping at %p\n", Process, Address); 275 KeBugCheck(MEMORY_MANAGEMENT); 276 } 277 278 /* Only for current process !!! */ 279 ASSERT(Process == PsGetCurrentProcess()); 280 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 281 282 ValidPde = MiIsPageTablePresent(Address); 283 if (ValidPde) 284 { 285 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 286 } 287 } 288 289 /* Get the PTE if we're having anything */ 290 if (ValidPde) 291 { 292 PointerPte = MiAddressToPte(Address); 293 OldPte.u.Long = InterlockedExchangePte(PointerPte, 0); 294 295 KeInvalidateTlbEntry(Address); 296 297 if (OldPte.u.Long != 0) 298 { 299 /* It must have been present, or not a swap entry */ 300 ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800)); 301 if (WasDirty != NULL) 302 { 303 *WasDirty = !!OldPte.u.Hard.Dirty; 304 } 305 if (Page != NULL) 306 { 307 *Page = OldPte.u.Hard.PageFrameNumber; 308 } 309 } 310 } 311 312 if (Process != NULL) 313 { 314 /* Remove PDE reference, if needed */ 315 if (OldPte.u.Long != 0) 316 { 317 if (MiDecrementPageTableReferences(Address) == 0) 318 { 319 KIRQL OldIrql = MiAcquirePfnLock(); 320 MiDeletePde(MiAddressToPde(Address), Process); 321 MiReleasePfnLock(OldIrql); 322 } 323 } 324 325 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 326 } 327 328 return OldPte.u.Long != 0; 329 } 330 331 332 VOID 333 NTAPI 334 MmDeletePageFileMapping( 335 PEPROCESS Process, 336 PVOID Address, 337 SWAPENTRY* SwapEntry) 338 { 339 PMMPTE PointerPte; 340 MMPTE OldPte; 341 342 /* This should not be called for kernel space anymore */ 343 ASSERT(Process != NULL); 344 ASSERT(Address < MmSystemRangeStart); 345 346 /* And we don't support deleting for other process */ 347 ASSERT(Process == PsGetCurrentProcess()); 348 349 /* And we should be at low IRQL */ 350 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 351 352 /* We are tinkering with the PDE here. Ensure it will be there */ 353 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 354 355 /* Callers must ensure there is actually something there */ 356 ASSERT(MiAddressToPde(Address)->u.Long != 0); 357 358 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 359 360 PointerPte = MiAddressToPte(Address); 361 OldPte.u.Long = InterlockedExchangePte(PointerPte, 0); 362 /* This must be a swap entry ! */ 363 if (!FlagOn(OldPte.u.Long, 0x800) || OldPte.u.Hard.Valid) 364 { 365 KeBugCheckEx(MEMORY_MANAGEMENT, OldPte.u.Long, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); 366 } 367 368 /* This used to be a non-zero PTE, now we can let the PDE go. */ 369 if (MiDecrementPageTableReferences(Address) == 0) 370 { 371 /* We can let it go */ 372 KIRQL OldIrql = MiAcquirePfnLock(); 373 MiDeletePde(MiPteToPde(PointerPte), Process); 374 MiReleasePfnLock(OldIrql); 375 } 376 377 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 378 379 *SwapEntry = OldPte.u.Long >> 1; 380 } 381 382 BOOLEAN 383 NTAPI 384 MmIsPagePresent(PEPROCESS Process, PVOID Address) 385 { 386 BOOLEAN Ret; 387 388 if (Address >= MmSystemRangeStart) 389 { 390 ASSERT(Process == NULL); 391 #if _MI_PAGING_LEVELS == 2 392 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 393 #else 394 if (!MiIsPdeForAddressValid(Address)) 395 #endif 396 { 397 /* It can't be present if there is no PDE */ 398 return FALSE; 399 } 400 401 return MiAddressToPte(Address)->u.Hard.Valid; 402 } 403 404 ASSERT(Process != NULL); 405 ASSERT(Process == PsGetCurrentProcess()); 406 407 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 408 409 if (!MiIsPageTablePresent(Address)) 410 { 411 /* It can't be present if there is no PDE */ 412 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 413 return FALSE; 414 } 415 416 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 417 418 Ret = MiAddressToPte(Address)->u.Hard.Valid; 419 420 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 421 422 return Ret; 423 } 424 425 BOOLEAN 426 NTAPI 427 MmIsDisabledPage(PEPROCESS Process, PVOID Address) 428 { 429 BOOLEAN Ret; 430 PMMPTE PointerPte; 431 432 if (Address >= MmSystemRangeStart) 433 { 434 ASSERT(Process == NULL); 435 #if _MI_PAGING_LEVELS == 2 436 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 437 #else 438 if (!MiIsPdeForAddressValid(Address)) 439 #endif 440 { 441 /* It's not disabled if it's not present */ 442 return FALSE; 443 } 444 } 445 else 446 { 447 ASSERT(Process != NULL); 448 ASSERT(Process == PsGetCurrentProcess()); 449 450 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 451 452 if (!MiIsPageTablePresent(Address)) 453 { 454 /* It can't be disabled if there is no PDE */ 455 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 456 return FALSE; 457 } 458 459 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 460 } 461 462 PointerPte = MiAddressToPte(Address); 463 Ret = !PointerPte->u.Hard.Valid 464 && !FlagOn(PointerPte->u.Long, 0x800) 465 && (PointerPte->u.Hard.PageFrameNumber != 0); 466 467 if (Address < MmSystemRangeStart) 468 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 469 470 return Ret; 471 } 472 473 BOOLEAN 474 NTAPI 475 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) 476 { 477 BOOLEAN Ret; 478 PMMPTE PointerPte; 479 480 /* We never set swap entries for kernel addresses */ 481 if (Address >= MmSystemRangeStart) 482 { 483 ASSERT(Process == NULL); 484 return FALSE; 485 } 486 487 ASSERT(Process != NULL); 488 ASSERT(Process == PsGetCurrentProcess()); 489 490 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 491 492 if (!MiIsPageTablePresent(Address)) 493 { 494 /* There can't be a swap entry if there is no PDE */ 495 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 496 return FALSE; 497 } 498 499 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 500 501 PointerPte = MiAddressToPte(Address); 502 Ret = !PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800); 503 504 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 505 506 return Ret; 507 } 508 509 VOID 510 NTAPI 511 MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry) 512 { 513 PMMPTE PointerPte; 514 515 /* We never set swap entries for kernel addresses */ 516 if (Address >= MmSystemRangeStart) 517 { 518 ASSERT(Process == NULL); 519 *SwapEntry = 0; 520 return; 521 } 522 523 ASSERT(Process != NULL); 524 ASSERT(Process == PsGetCurrentProcess()); 525 526 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 527 528 if (!MiIsPageTablePresent(Address)) 529 { 530 /* There can't be a swap entry if there is no PDE */ 531 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 532 *SwapEntry = 0; 533 return; 534 } 535 536 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 537 538 PointerPte = MiAddressToPte(Address); 539 if (!PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800)) 540 *SwapEntry = PointerPte->u.Long >> 1; 541 else 542 *SwapEntry = 0; 543 544 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 545 } 546 547 NTSTATUS 548 NTAPI 549 MmCreatePageFileMapping(PEPROCESS Process, 550 PVOID Address, 551 SWAPENTRY SwapEntry) 552 { 553 PMMPTE PointerPte; 554 ULONG_PTR Pte; 555 556 /* This should not be called for kernel space anymore */ 557 ASSERT(Process != NULL); 558 ASSERT(Address < MmSystemRangeStart); 559 560 /* And we don't support creating for other process */ 561 ASSERT(Process == PsGetCurrentProcess()); 562 563 if (SwapEntry & (1 << 31)) 564 { 565 KeBugCheck(MEMORY_MANAGEMENT); 566 } 567 568 /* We are tinkering with the PDE here. Ensure it will be there */ 569 ASSERT(Process == PsGetCurrentProcess()); 570 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 571 572 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 573 574 PointerPte = MiAddressToPte(Address); 575 Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1); 576 if (Pte != 0) 577 { 578 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); 579 } 580 581 /* This used to be a 0 PTE, now we need a valid PDE to keep it around */ 582 MiIncrementPageTableReferences(Address); 583 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 584 585 return STATUS_SUCCESS; 586 } 587 588 589 NTSTATUS 590 NTAPI 591 MmCreateVirtualMappingUnsafe(PEPROCESS Process, 592 PVOID Address, 593 ULONG flProtect, 594 PFN_NUMBER Page) 595 { 596 ULONG ProtectionMask; 597 PMMPTE PointerPte; 598 MMPTE TempPte; 599 ULONG_PTR Pte; 600 601 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n", 602 Process, Address, flProtect, Page); 603 604 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0); 605 606 ProtectionMask = MiMakeProtectionMask(flProtect); 607 /* Caller must have checked ! */ 608 ASSERT(ProtectionMask != MM_INVALID_PROTECTION); 609 ASSERT(ProtectionMask != MM_NOACCESS); 610 ASSERT(ProtectionMask != MM_ZERO_ACCESS); 611 612 /* Make sure our PDE is valid, and that everything is going fine */ 613 if (Process == NULL) 614 { 615 /* We don't support this in legacy Mm for kernel mappings */ 616 ASSERT(ProtectionMask != MM_WRITECOPY); 617 ASSERT(ProtectionMask != MM_EXECUTE_WRITECOPY); 618 619 if (Address < MmSystemRangeStart) 620 { 621 DPRINT1("NULL process given for user-mode mapping at %p\n", Address); 622 KeBugCheck(MEMORY_MANAGEMENT); 623 } 624 #if _MI_PAGING_LEVELS == 2 625 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 626 MiFillSystemPageDirectory(Address, PAGE_SIZE); 627 #endif 628 } 629 else 630 { 631 if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart) 632 { 633 DPRINT1("Process %p given for kernel-mode mapping at %p -- 1 page starting at %Ix\n", 634 Process, Address, Page); 635 KeBugCheck(MEMORY_MANAGEMENT); 636 } 637 638 /* Only for current process !!! */ 639 ASSERT(Process == PsGetCurrentProcess()); 640 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 641 642 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 643 } 644 645 PointerPte = MiAddressToPte(Address); 646 647 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page); 648 649 Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long); 650 /* There should not have been anything valid here */ 651 if (Pte != 0) 652 { 653 DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address); 654 KeBugCheck(MEMORY_MANAGEMENT); 655 } 656 657 /* We don't need to flush the TLB here because it only caches valid translations 658 * and we're moving this PTE from invalid to valid so it can't be cached right now */ 659 660 if (Address < MmSystemRangeStart) 661 { 662 /* Add PDE reference */ 663 MiIncrementPageTableReferences(Address); 664 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 665 } 666 667 return(STATUS_SUCCESS); 668 } 669 670 NTSTATUS 671 NTAPI 672 MmCreateVirtualMapping(PEPROCESS Process, 673 PVOID Address, 674 ULONG flProtect, 675 PFN_NUMBER Page) 676 { 677 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0); 678 if (!MmIsPageInUse(Page)) 679 { 680 DPRINT1("Page %lx is not in use\n", Page); 681 KeBugCheck(MEMORY_MANAGEMENT); 682 } 683 684 return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page); 685 } 686 687 ULONG 688 NTAPI 689 MmGetPageProtect(PEPROCESS Process, PVOID Address) 690 { 691 PMMPTE PointerPte; 692 ULONG Protect; 693 694 if (Address >= MmSystemRangeStart) 695 { 696 ASSERT(Process == NULL); 697 698 #if _MI_PAGING_LEVELS == 2 699 if (!MiSynchronizeSystemPde(MiAddressToPde(Address))) 700 #else 701 if (!MiIsPdeForAddressValid(Address)) 702 #endif 703 { 704 return PAGE_NOACCESS; 705 } 706 } 707 else 708 { 709 ASSERT(Address < MmSystemRangeStart); 710 ASSERT(Process != NULL); 711 712 ASSERT(Process == PsGetCurrentProcess()); 713 714 MiLockProcessWorkingSetShared(Process, PsGetCurrentThread()); 715 716 if (!MiIsPageTablePresent(Address)) 717 { 718 /* It can't be present if there is no PDE */ 719 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 720 return PAGE_NOACCESS; 721 } 722 723 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 724 } 725 726 PointerPte = MiAddressToPte(Address); 727 728 if (!PointerPte->u.Flush.Valid) 729 { 730 Protect = PAGE_NOACCESS; 731 } 732 else 733 { 734 if (PointerPte->u.Flush.CopyOnWrite) 735 Protect = PAGE_WRITECOPY; 736 else if (PointerPte->u.Flush.Write) 737 Protect = PAGE_READWRITE; 738 else 739 Protect = PAGE_READONLY; 740 #if _MI_PAGING_LEVELS >= 3 741 /* PAE & AMD64 long mode support NoExecute bit */ 742 if (!PointerPte->u.Flush.NoExecute) 743 Protect <<= 4; 744 #endif 745 if (PointerPte->u.Flush.CacheDisable) 746 Protect |= PAGE_NOCACHE; 747 if (PointerPte->u.Flush.WriteThrough) 748 Protect |= PAGE_WRITETHROUGH; 749 } 750 751 if (Address < MmSystemRangeStart) 752 MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread()); 753 754 return(Protect); 755 } 756 757 VOID 758 NTAPI 759 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect) 760 { 761 ULONG ProtectionMask; 762 PMMPTE PointerPte; 763 MMPTE TempPte, OldPte; 764 765 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n", 766 Process, Address, flProtect); 767 768 ASSERT(Process != NULL); 769 ASSERT(Address < MmSystemRangeStart); 770 771 ASSERT(Process == PsGetCurrentProcess()); 772 773 ProtectionMask = MiMakeProtectionMask(flProtect); 774 /* Caller must have checked ! */ 775 ASSERT(ProtectionMask != MM_INVALID_PROTECTION); 776 777 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 778 779 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 780 781 PointerPte = MiAddressToPte(Address); 782 783 /* Sanity check */ 784 ASSERT(PointerPte->u.Hard.Owner == 1); 785 786 TempPte.u.Long = 0; 787 TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber; 788 TempPte.u.Long |= MmProtectToPteMask[ProtectionMask]; 789 TempPte.u.Hard.Owner = 1; 790 791 /* Only set valid bit if we have to */ 792 if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE)) 793 TempPte.u.Hard.Valid = 1; 794 795 /* Keep dirty & accessed bits */ 796 TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed; 797 TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty; 798 799 OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long); 800 801 // We should be able to bring a page back from PAGE_NOACCESS 802 if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0))) 803 { 804 DPRINT1("Invalid Pte %lx\n", OldPte.u.Long); 805 KeBugCheck(MEMORY_MANAGEMENT); 806 } 807 808 if (OldPte.u.Long != TempPte.u.Long) 809 KeInvalidateTlbEntry(Address); 810 811 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 812 } 813 814 VOID 815 NTAPI 816 MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit) 817 { 818 PMMPTE PointerPte; 819 820 DPRINT("MmSetDirtyBit(Process %p Address %p Bit %x)\n", 821 Process, Address, Bit); 822 823 ASSERT(Process != NULL); 824 ASSERT(Address < MmSystemRangeStart); 825 826 ASSERT(Process == PsGetCurrentProcess()); 827 828 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 829 830 MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL); 831 832 PointerPte = MiAddressToPte(Address); 833 // We shouldnl't set dirty bit on non-mapped adresses 834 if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0))) 835 { 836 DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long); 837 KeBugCheck(MEMORY_MANAGEMENT); 838 } 839 840 PointerPte->u.Hard.Dirty = !!Bit; 841 842 if (!Bit) 843 KeInvalidateTlbEntry(Address); 844 845 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 846 } 847 848 CODE_SEG("INIT") 849 VOID 850 NTAPI 851 MmInitGlobalKernelPageDirectory(VOID) 852 { 853 /* Nothing to do here */ 854 } 855 856 #ifdef _M_IX86 857 BOOLEAN 858 Mmi386MakeKernelPageTableGlobal(PVOID Address) 859 { 860 PMMPDE PointerPde = MiAddressToPde(Address); 861 PMMPTE PointerPte = MiAddressToPte(Address); 862 863 if (PointerPde->u.Hard.Valid == 0) 864 { 865 if (!MiSynchronizeSystemPde(PointerPde)) 866 return FALSE; 867 return PointerPte->u.Hard.Valid != 0; 868 } 869 return FALSE; 870 } 871 #endif 872 873 /* EOF */ 874