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