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 #if defined (ALLOC_PRAGMA) 19 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory) 20 #endif 21 22 #define ADDR_TO_PDE_OFFSET MiAddressToPdeOffset 23 #define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (1024 * PAGE_SIZE)) 24 25 /* GLOBALS *****************************************************************/ 26 27 #define PA_BIT_PRESENT (0) 28 #define PA_BIT_READWRITE (1) 29 #define PA_BIT_USER (2) 30 #define PA_BIT_WT (3) 31 #define PA_BIT_CD (4) 32 #define PA_BIT_ACCESSED (5) 33 #define PA_BIT_DIRTY (6) 34 #define PA_BIT_GLOBAL (8) 35 36 #define PA_PRESENT (1 << PA_BIT_PRESENT) 37 #define PA_READWRITE (1 << PA_BIT_READWRITE) 38 #define PA_USER (1 << PA_BIT_USER) 39 #define PA_DIRTY (1 << PA_BIT_DIRTY) 40 #define PA_WT (1 << PA_BIT_WT) 41 #define PA_CD (1 << PA_BIT_CD) 42 #define PA_ACCESSED (1 << PA_BIT_ACCESSED) 43 #define PA_GLOBAL (1 << PA_BIT_GLOBAL) 44 45 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPER_SPACE && (ULONG)(v) <= HYPER_SPACE_END)) 46 47 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT) 48 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT) 49 50 #define PAGE_MASK(x) ((x)&(~0xfff)) 51 52 const 53 ULONG 54 MmProtectToPteMask[32] = 55 { 56 // 57 // These are the base MM_ protection flags 58 // 59 0, 60 PTE_READONLY | PTE_ENABLE_CACHE, 61 PTE_EXECUTE | PTE_ENABLE_CACHE, 62 PTE_EXECUTE_READ | PTE_ENABLE_CACHE, 63 PTE_READWRITE | PTE_ENABLE_CACHE, 64 PTE_WRITECOPY | PTE_ENABLE_CACHE, 65 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE, 66 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE, 67 // 68 // These OR in the MM_NOCACHE flag 69 // 70 0, 71 PTE_READONLY | PTE_DISABLE_CACHE, 72 PTE_EXECUTE | PTE_DISABLE_CACHE, 73 PTE_EXECUTE_READ | PTE_DISABLE_CACHE, 74 PTE_READWRITE | PTE_DISABLE_CACHE, 75 PTE_WRITECOPY | PTE_DISABLE_CACHE, 76 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE, 77 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE, 78 // 79 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM 80 // 81 0, 82 PTE_READONLY | PTE_ENABLE_CACHE, 83 PTE_EXECUTE | PTE_ENABLE_CACHE, 84 PTE_EXECUTE_READ | PTE_ENABLE_CACHE, 85 PTE_READWRITE | PTE_ENABLE_CACHE, 86 PTE_WRITECOPY | PTE_ENABLE_CACHE, 87 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE, 88 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE, 89 // 90 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining? 91 // 92 0, 93 PTE_READONLY | PTE_WRITECOMBINED_CACHE, 94 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE, 95 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE, 96 PTE_READWRITE | PTE_WRITECOMBINED_CACHE, 97 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE, 98 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE, 99 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE, 100 }; 101 102 const 103 ULONG MmProtectToValue[32] = 104 { 105 PAGE_NOACCESS, 106 PAGE_READONLY, 107 PAGE_EXECUTE, 108 PAGE_EXECUTE_READ, 109 PAGE_READWRITE, 110 PAGE_WRITECOPY, 111 PAGE_EXECUTE_READWRITE, 112 PAGE_EXECUTE_WRITECOPY, 113 PAGE_NOACCESS, 114 PAGE_NOCACHE | PAGE_READONLY, 115 PAGE_NOCACHE | PAGE_EXECUTE, 116 PAGE_NOCACHE | PAGE_EXECUTE_READ, 117 PAGE_NOCACHE | PAGE_READWRITE, 118 PAGE_NOCACHE | PAGE_WRITECOPY, 119 PAGE_NOCACHE | PAGE_EXECUTE_READWRITE, 120 PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY, 121 PAGE_NOACCESS, 122 PAGE_GUARD | PAGE_READONLY, 123 PAGE_GUARD | PAGE_EXECUTE, 124 PAGE_GUARD | PAGE_EXECUTE_READ, 125 PAGE_GUARD | PAGE_READWRITE, 126 PAGE_GUARD | PAGE_WRITECOPY, 127 PAGE_GUARD | PAGE_EXECUTE_READWRITE, 128 PAGE_GUARD | PAGE_EXECUTE_WRITECOPY, 129 PAGE_NOACCESS, 130 PAGE_WRITECOMBINE | PAGE_READONLY, 131 PAGE_WRITECOMBINE | PAGE_EXECUTE, 132 PAGE_WRITECOMBINE | PAGE_EXECUTE_READ, 133 PAGE_WRITECOMBINE | PAGE_READWRITE, 134 PAGE_WRITECOMBINE | PAGE_WRITECOPY, 135 PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE, 136 PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY 137 }; 138 139 /* FUNCTIONS ***************************************************************/ 140 141 static BOOLEAN MmUnmapPageTable(PULONG Pt); 142 143 VOID 144 MiFlushTlb(PULONG Pt, PVOID Address) 145 { 146 if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart) 147 { 148 KeInvalidateTlbEntry(Address); 149 } 150 } 151 152 static ULONG 153 ProtectToPTE(ULONG flProtect) 154 { 155 ULONG Attributes = 0; 156 157 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD)) 158 { 159 Attributes = 0; 160 } 161 else if (flProtect & PAGE_IS_WRITABLE) 162 { 163 Attributes = PA_PRESENT | PA_READWRITE; 164 } 165 else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE)) 166 { 167 Attributes = PA_PRESENT; 168 } 169 else 170 { 171 DPRINT1("Unknown main protection type.\n"); 172 KeBugCheck(MEMORY_MANAGEMENT); 173 } 174 175 if (flProtect & PAGE_SYSTEM) 176 { 177 } 178 else 179 { 180 Attributes = Attributes | PA_USER; 181 } 182 if (flProtect & PAGE_NOCACHE) 183 { 184 Attributes = Attributes | PA_CD; 185 } 186 if (flProtect & PAGE_WRITETHROUGH) 187 { 188 Attributes = Attributes | PA_WT; 189 } 190 return(Attributes); 191 } 192 193 NTSTATUS 194 NTAPI 195 MiDispatchFault(IN ULONG FaultCode, 196 IN PVOID Address, 197 IN PMMPTE PointerPte, 198 IN PMMPTE PointerProtoPte, 199 IN BOOLEAN Recursive, 200 IN PEPROCESS Process, 201 IN PVOID TrapInformation, 202 IN PVOID Vad); 203 204 NTSTATUS 205 NTAPI 206 MiFillSystemPageDirectory(IN PVOID Base, 207 IN SIZE_T NumberOfBytes); 208 209 static PULONG 210 MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create) 211 { 212 PFN_NUMBER Pfn; 213 PULONG Pt; 214 PMMPDE PointerPde; 215 216 if (Address < MmSystemRangeStart) 217 { 218 /* We should have a process for user land addresses */ 219 ASSERT(Process != NULL); 220 221 if(Process != PsGetCurrentProcess()) 222 { 223 PMMPDE PdeBase; 224 ULONG PdeOffset = MiGetPdeOffset(Address); 225 226 /* Nobody but page fault should ask for creating the PDE, 227 * Which imples that Process is the current one */ 228 ASSERT(Create == FALSE); 229 230 PdeBase = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0])); 231 if (PdeBase == NULL) 232 { 233 KeBugCheck(MEMORY_MANAGEMENT); 234 } 235 PointerPde = PdeBase + PdeOffset; 236 if (PointerPde->u.Hard.Valid == 0) 237 { 238 MmDeleteHyperspaceMapping(PdeBase); 239 return NULL; 240 } 241 else 242 { 243 Pfn = PointerPde->u.Hard.PageFrameNumber; 244 } 245 MmDeleteHyperspaceMapping(PdeBase); 246 Pt = MmCreateHyperspaceMapping(Pfn); 247 if (Pt == NULL) 248 { 249 KeBugCheck(MEMORY_MANAGEMENT); 250 } 251 return Pt + MiAddressToPteOffset(Address); 252 } 253 /* This is for our process */ 254 PointerPde = MiAddressToPde(Address); 255 Pt = (PULONG)MiAddressToPte(Address); 256 if (PointerPde->u.Hard.Valid == 0) 257 { 258 NTSTATUS Status; 259 if (Create == FALSE) 260 { 261 return NULL; 262 } 263 ASSERT(PointerPde->u.Long == 0); 264 265 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde); 266 // Tiny HACK: Parameter 1 is the architecture specific FaultCode for an access violation (i.e. page is present) 267 268 /* Lock the working set, as this will add this address to it */ 269 MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 270 271 Status = MiDispatchFault(0x1, 272 Pt, 273 PointerPde, 274 NULL, 275 FALSE, 276 PsGetCurrentProcess(), 277 NULL, 278 NULL); 279 DBG_UNREFERENCED_LOCAL_VARIABLE(Status); 280 ASSERT(KeAreAllApcsDisabled() == TRUE); 281 ASSERT(PointerPde->u.Hard.Valid == 1); 282 283 MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread()); 284 } 285 return (PULONG)MiAddressToPte(Address); 286 } 287 288 /* This is for kernel land address */ 289 ASSERT(Process == NULL); 290 PointerPde = MiAddressToPde(Address); 291 Pt = (PULONG)MiAddressToPte(Address); 292 if (PointerPde->u.Hard.Valid == 0) 293 { 294 /* Let ARM3 synchronize the PDE */ 295 if(!MiSynchronizeSystemPde(PointerPde)) 296 { 297 /* PDE (still) not valid, let ARM3 allocate one if asked */ 298 if(Create == FALSE) 299 return NULL; 300 MiFillSystemPageDirectory(Address, PAGE_SIZE); 301 } 302 } 303 return Pt; 304 } 305 306 static BOOLEAN MmUnmapPageTable(PULONG Pt) 307 { 308 if (!IS_HYPERSPACE(Pt)) 309 { 310 return TRUE; 311 } 312 313 if (Pt) 314 { 315 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt)); 316 } 317 return FALSE; 318 } 319 320 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address) 321 { 322 ULONG Pte; 323 PULONG Pt; 324 325 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 326 if (Pt) 327 { 328 Pte = *Pt; 329 MmUnmapPageTable(Pt); 330 return Pte; 331 } 332 return 0; 333 } 334 335 PFN_NUMBER 336 NTAPI 337 MmGetPfnForProcess(PEPROCESS Process, 338 PVOID Address) 339 { 340 ULONG Entry; 341 Entry = MmGetPageEntryForProcess(Process, Address); 342 if (!(Entry & PA_PRESENT)) 343 { 344 return 0; 345 } 346 return(PTE_TO_PFN(Entry)); 347 } 348 349 VOID 350 NTAPI 351 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, 352 BOOLEAN* WasDirty, PPFN_NUMBER Page) 353 /* 354 * FUNCTION: Delete a virtual mapping 355 */ 356 { 357 BOOLEAN WasValid = FALSE; 358 PFN_NUMBER Pfn; 359 ULONG Pte; 360 PULONG Pt; 361 362 DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", 363 Process, Address, WasDirty, Page); 364 365 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 366 367 if (Pt == NULL) 368 { 369 if (WasDirty != NULL) 370 { 371 *WasDirty = FALSE; 372 } 373 if (Page != NULL) 374 { 375 *Page = 0; 376 } 377 return; 378 } 379 380 /* 381 * Atomically set the entry to zero and get the old value. 382 */ 383 Pte = InterlockedExchangePte(Pt, 0); 384 385 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with 386 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */ 387 WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800)); 388 if (WasValid) 389 { 390 /* Flush the TLB since we transitioned this PTE 391 * from valid to invalid so any stale translations 392 * are removed from the cache */ 393 MiFlushTlb(Pt, Address); 394 395 if (Address < MmSystemRangeStart) 396 { 397 /* Remove PDE reference */ 398 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--; 399 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_PER_PAGE); 400 } 401 402 Pfn = PTE_TO_PFN(Pte); 403 } 404 else 405 { 406 MmUnmapPageTable(Pt); 407 Pfn = 0; 408 } 409 410 /* 411 * Return some information to the caller 412 */ 413 if (WasDirty != NULL) 414 { 415 *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE; 416 } 417 if (Page != NULL) 418 { 419 *Page = Pfn; 420 } 421 } 422 423 VOID 424 NTAPI 425 MmGetPageFileMapping(PEPROCESS Process, PVOID Address, 426 SWAPENTRY* SwapEntry) 427 /* 428 * FUNCTION: Get a page file mapping 429 */ 430 { 431 ULONG Entry = MmGetPageEntryForProcess(Process, Address); 432 *SwapEntry = Entry >> 1; 433 } 434 435 VOID 436 NTAPI 437 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address, 438 SWAPENTRY* SwapEntry) 439 /* 440 * FUNCTION: Delete a virtual mapping 441 */ 442 { 443 ULONG Pte; 444 PULONG Pt; 445 446 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 447 448 if (Pt == NULL) 449 { 450 *SwapEntry = 0; 451 return; 452 } 453 454 /* 455 * Atomically set the entry to zero and get the old value. 456 */ 457 Pte = InterlockedExchangePte(Pt, 0); 458 459 if (Address < MmSystemRangeStart) 460 { 461 /* Remove PDE reference */ 462 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--; 463 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_PER_PAGE); 464 } 465 466 /* We don't need to flush here because page file entries 467 * are invalid translations, so the processor won't cache them */ 468 MmUnmapPageTable(Pt); 469 470 if ((Pte & PA_PRESENT) || !(Pte & 0x800)) 471 { 472 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte); 473 KeBugCheck(MEMORY_MANAGEMENT); 474 } 475 476 /* 477 * Return some information to the caller 478 */ 479 *SwapEntry = Pte >> 1; 480 } 481 482 BOOLEAN 483 Mmi386MakeKernelPageTableGlobal(PVOID Address) 484 { 485 PMMPDE PointerPde = MiAddressToPde(Address); 486 PMMPTE PointerPte = MiAddressToPte(Address); 487 488 if (PointerPde->u.Hard.Valid == 0) 489 { 490 if(!MiSynchronizeSystemPde(PointerPde)) 491 return FALSE; 492 return PointerPte->u.Hard.Valid != 0; 493 } 494 return FALSE; 495 } 496 497 BOOLEAN 498 NTAPI 499 MmIsDirtyPage(PEPROCESS Process, PVOID Address) 500 { 501 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE; 502 } 503 504 VOID 505 NTAPI 506 MmSetCleanPage(PEPROCESS Process, PVOID Address) 507 { 508 PULONG Pt; 509 ULONG Pte; 510 511 if (Address < MmSystemRangeStart && Process == NULL) 512 { 513 DPRINT1("MmSetCleanPage is called for user space without a process.\n"); 514 KeBugCheck(MEMORY_MANAGEMENT); 515 } 516 517 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 518 if (Pt == NULL) 519 { 520 KeBugCheck(MEMORY_MANAGEMENT); 521 } 522 523 do 524 { 525 Pte = *Pt; 526 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte)); 527 528 if (!(Pte & PA_PRESENT)) 529 { 530 KeBugCheck(MEMORY_MANAGEMENT); 531 } 532 else if (Pte & PA_DIRTY) 533 { 534 MiFlushTlb(Pt, Address); 535 } 536 else 537 { 538 MmUnmapPageTable(Pt); 539 } 540 } 541 542 VOID 543 NTAPI 544 MmSetDirtyPage(PEPROCESS Process, PVOID Address) 545 { 546 PULONG Pt; 547 ULONG Pte; 548 549 if (Address < MmSystemRangeStart && Process == NULL) 550 { 551 DPRINT1("MmSetDirtyPage is called for user space without a process.\n"); 552 KeBugCheck(MEMORY_MANAGEMENT); 553 } 554 555 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 556 if (Pt == NULL) 557 { 558 KeBugCheck(MEMORY_MANAGEMENT); 559 } 560 561 do 562 { 563 Pte = *Pt; 564 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte)); 565 566 if (!(Pte & PA_PRESENT)) 567 { 568 KeBugCheck(MEMORY_MANAGEMENT); 569 } 570 else 571 { 572 /* The processor will never clear this bit itself, therefore 573 * we do not need to flush the TLB here when setting it */ 574 MmUnmapPageTable(Pt); 575 } 576 } 577 578 BOOLEAN 579 NTAPI 580 MmIsPagePresent(PEPROCESS Process, PVOID Address) 581 { 582 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT; 583 } 584 585 BOOLEAN 586 NTAPI 587 MmIsDisabledPage(PEPROCESS Process, PVOID Address) 588 { 589 ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address); 590 return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT); 591 } 592 593 BOOLEAN 594 NTAPI 595 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) 596 { 597 ULONG Entry; 598 Entry = MmGetPageEntryForProcess(Process, Address); 599 return !(Entry & PA_PRESENT) && (Entry & 0x800); 600 } 601 602 NTSTATUS 603 NTAPI 604 MmCreatePageFileMapping(PEPROCESS Process, 605 PVOID Address, 606 SWAPENTRY SwapEntry) 607 { 608 PULONG Pt; 609 ULONG Pte; 610 611 if (Process == NULL && Address < MmSystemRangeStart) 612 { 613 DPRINT1("No process\n"); 614 KeBugCheck(MEMORY_MANAGEMENT); 615 } 616 if (Process != NULL && Address >= MmSystemRangeStart) 617 { 618 DPRINT1("Setting kernel address with process context\n"); 619 KeBugCheck(MEMORY_MANAGEMENT); 620 } 621 622 if (SwapEntry & (1 << 31)) 623 { 624 KeBugCheck(MEMORY_MANAGEMENT); 625 } 626 627 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 628 if (Pt == NULL) 629 { 630 /* Nobody should page out an address that hasn't even been mapped */ 631 /* But we might place a wait entry first, requiring the page table */ 632 if (SwapEntry != MM_WAIT_ENTRY) 633 { 634 KeBugCheck(MEMORY_MANAGEMENT); 635 } 636 Pt = MmGetPageTableForProcess(Process, Address, TRUE); 637 } 638 Pte = InterlockedExchangePte(Pt, SwapEntry << 1); 639 if (Pte != 0) 640 { 641 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); 642 } 643 644 if (Address < MmSystemRangeStart) 645 { 646 /* Add PDE reference */ 647 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++; 648 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_PER_PAGE); 649 } 650 651 /* We don't need to flush the TLB here because it 652 * only caches valid translations and a zero PTE 653 * is not a valid translation */ 654 MmUnmapPageTable(Pt); 655 656 return(STATUS_SUCCESS); 657 } 658 659 660 NTSTATUS 661 NTAPI 662 MmCreateVirtualMappingUnsafe(PEPROCESS Process, 663 PVOID Address, 664 ULONG flProtect, 665 PPFN_NUMBER Pages, 666 ULONG PageCount) 667 { 668 ULONG Attributes; 669 PVOID Addr; 670 ULONG i; 671 ULONG oldPdeOffset, PdeOffset; 672 PULONG Pt = NULL; 673 ULONG Pte; 674 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n", 675 Process, Address, flProtect, Pages, *Pages, PageCount); 676 677 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0); 678 679 if (Process == NULL) 680 { 681 if (Address < MmSystemRangeStart) 682 { 683 DPRINT1("NULL process given for user-mode mapping at %p -- %lu pages starting at %Ix\n", Address, PageCount, *Pages); 684 KeBugCheck(MEMORY_MANAGEMENT); 685 } 686 if (PageCount > 0x10000 || 687 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000) 688 { 689 DPRINT1("Page count too large for kernel-mode mapping at %p -- %lu pages starting at %Ix\n", Address, PageCount, *Pages); 690 KeBugCheck(MEMORY_MANAGEMENT); 691 } 692 } 693 else 694 { 695 if (Address >= MmSystemRangeStart) 696 { 697 DPRINT1("Process %p given for kernel-mode mapping at %p -- %lu pages starting at %Ix\n", Process, Address, PageCount, *Pages); 698 KeBugCheck(MEMORY_MANAGEMENT); 699 } 700 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE || 701 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 702 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE) 703 { 704 DPRINT1("Page count too large for process %p user-mode mapping at %p -- %lu pages starting at %Ix\n", Process, Address, PageCount, *Pages); 705 KeBugCheck(MEMORY_MANAGEMENT); 706 } 707 } 708 709 Attributes = ProtectToPTE(flProtect); 710 Attributes &= 0xfff; 711 if (Address >= MmSystemRangeStart) 712 { 713 Attributes &= ~PA_USER; 714 } 715 else 716 { 717 Attributes |= PA_USER; 718 } 719 720 Addr = Address; 721 /* MmGetPageTableForProcess should be called on the first run, so 722 * let this trigger it */ 723 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1; 724 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE)) 725 { 726 if (!(Attributes & PA_PRESENT) && Pages[i] != 0) 727 { 728 DPRINT1("Setting physical address but not allowing access at address " 729 "0x%p with attributes %x/%x.\n", 730 Addr, Attributes, flProtect); 731 KeBugCheck(MEMORY_MANAGEMENT); 732 } 733 PdeOffset = ADDR_TO_PDE_OFFSET(Addr); 734 if (oldPdeOffset != PdeOffset) 735 { 736 if(Pt) MmUnmapPageTable(Pt); 737 Pt = MmGetPageTableForProcess(Process, Addr, TRUE); 738 if (Pt == NULL) 739 { 740 KeBugCheck(MEMORY_MANAGEMENT); 741 } 742 } 743 else 744 { 745 Pt++; 746 } 747 oldPdeOffset = PdeOffset; 748 749 Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes); 750 751 /* There should not be anything valid here */ 752 if (Pte != 0) 753 { 754 DPRINT1("Bad PTE %lx at %p for %p + %lu\n", Pte, Pt, Address, i); 755 KeBugCheck(MEMORY_MANAGEMENT); 756 } 757 758 /* We don't need to flush the TLB here because it only caches valid translations 759 * and we're moving this PTE from invalid to valid so it can't be cached right now */ 760 761 if (Addr < MmSystemRangeStart) 762 { 763 /* Add PDE reference */ 764 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++; 765 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_PER_PAGE); 766 } 767 } 768 769 ASSERT(Addr > Address); 770 MmUnmapPageTable(Pt); 771 772 return(STATUS_SUCCESS); 773 } 774 775 NTSTATUS 776 NTAPI 777 MmCreateVirtualMapping(PEPROCESS Process, 778 PVOID Address, 779 ULONG flProtect, 780 PPFN_NUMBER Pages, 781 ULONG PageCount) 782 { 783 ULONG i; 784 785 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0); 786 for (i = 0; i < PageCount; i++) 787 { 788 if (!MmIsPageInUse(Pages[i])) 789 { 790 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i])); 791 KeBugCheck(MEMORY_MANAGEMENT); 792 } 793 } 794 795 return(MmCreateVirtualMappingUnsafe(Process, 796 Address, 797 flProtect, 798 Pages, 799 PageCount)); 800 } 801 802 ULONG 803 NTAPI 804 MmGetPageProtect(PEPROCESS Process, PVOID Address) 805 { 806 ULONG Entry; 807 ULONG Protect; 808 809 Entry = MmGetPageEntryForProcess(Process, Address); 810 811 812 if (!(Entry & PA_PRESENT)) 813 { 814 Protect = PAGE_NOACCESS; 815 } 816 else 817 { 818 if (Entry & PA_READWRITE) 819 { 820 Protect = PAGE_READWRITE; 821 } 822 else 823 { 824 Protect = PAGE_EXECUTE_READ; 825 } 826 if (Entry & PA_CD) 827 { 828 Protect |= PAGE_NOCACHE; 829 } 830 if (Entry & PA_WT) 831 { 832 Protect |= PAGE_WRITETHROUGH; 833 } 834 if (!(Entry & PA_USER)) 835 { 836 Protect |= PAGE_SYSTEM; 837 } 838 839 } 840 return(Protect); 841 } 842 843 VOID 844 NTAPI 845 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect) 846 { 847 ULONG Attributes = 0; 848 PULONG Pt; 849 ULONG Pte; 850 851 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n", 852 Process, Address, flProtect); 853 854 Attributes = ProtectToPTE(flProtect); 855 856 Attributes &= 0xfff; 857 if (Address >= MmSystemRangeStart) 858 { 859 Attributes &= ~PA_USER; 860 } 861 else 862 { 863 Attributes |= PA_USER; 864 } 865 866 Pt = MmGetPageTableForProcess(Process, Address, FALSE); 867 if (Pt == NULL) 868 { 869 KeBugCheck(MEMORY_MANAGEMENT); 870 } 871 Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY))); 872 873 // We should be able to bring a page back from PAGE_NOACCESS 874 if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT)) 875 { 876 DPRINT1("Invalid Pte %lx\n", Pte); 877 KeBugCheck(MEMORY_MANAGEMENT); 878 } 879 880 if((Pte & Attributes) != Attributes) 881 MiFlushTlb(Pt, Address); 882 else 883 MmUnmapPageTable(Pt); 884 } 885 886 INIT_FUNCTION 887 VOID 888 NTAPI 889 MmInitGlobalKernelPageDirectory(VOID) 890 { 891 /* Nothing to do here */ 892 } 893 894 /* EOF */ 895