Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

mmsup.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 mmsup.c 00008 00009 Abstract: 00010 00011 This module contains the various routines for miscellaneous support 00012 operations for memory management. 00013 00014 Author: 00015 00016 Lou Perazzoli (loup) 31-Aug-1989 00017 00018 Revision History: 00019 00020 Landy Wang (landyw) 08-April-1998 : Modifications for 3-level 64-bit NT. 00021 00022 --*/ 00023 00024 #include "mi.h" 00025 00026 #ifdef ALLOC_PRAGMA 00027 #pragma alloc_text(PAGE, MmHibernateInformation) 00028 #endif 00029 00030 #if _WIN64 00031 #if DBGXX 00032 VOID 00033 MiCheckPageTableTrim( 00034 IN PMMPTE PointerPte 00035 ); 00036 #endif 00037 #endif 00038 00039 00040 ULONG 00041 FASTCALL 00042 MiIsPteDecommittedPage ( 00043 IN PMMPTE PointerPte 00044 ) 00045 00046 /*++ 00047 00048 Routine Description: 00049 00050 This function checks the contents of a PTE to determine if the 00051 PTE is explicitly decommitted. 00052 00053 If the PTE is a prototype PTE and the protection is not in the 00054 prototype PTE, the value FALSE is returned. 00055 00056 Arguments: 00057 00058 PointerPte - Supplies a pointer to the PTE to examine. 00059 00060 Return Value: 00061 00062 TRUE if the PTE is in the explicit decommitted state. 00063 FALSE if the PTE is not in the explicit decommitted state. 00064 00065 Environment: 00066 00067 Kernel mode, APCs disabled, WorkingSetLock held. 00068 00069 --*/ 00070 00071 { 00072 MMPTE PteContents; 00073 00074 PteContents = *PointerPte; 00075 00076 // 00077 // If the protection in the PTE is not decommitted, return false. 00078 // 00079 00080 if (PteContents.u.Soft.Protection != MM_DECOMMIT) { 00081 return FALSE; 00082 } 00083 00084 // 00085 // Check to make sure the protection field is really being interpreted 00086 // correctly. 00087 // 00088 00089 if (PteContents.u.Hard.Valid == 1) { 00090 00091 // 00092 // The PTE is valid and therefore cannot be decommitted. 00093 // 00094 00095 return FALSE; 00096 } 00097 00098 if ((PteContents.u.Soft.Prototype == 1) && 00099 (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)) { 00100 00101 // 00102 // The PTE's protection is not known as it is in 00103 // prototype PTE format. Return FALSE. 00104 // 00105 00106 return FALSE; 00107 } 00108 00109 // 00110 // It is a decommitted PTE. 00111 // 00112 00113 return TRUE; 00114 } 00115 00116 // 00117 // Data for is protection compatible. 00118 // 00119 00120 ULONG MmCompatibleProtectionMask[8] = { 00121 PAGE_NOACCESS, 00122 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY, 00123 PAGE_NOACCESS | PAGE_EXECUTE, 00124 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE | 00125 PAGE_EXECUTE_READ, 00126 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE, 00127 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY, 00128 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE | 00129 PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | 00130 PAGE_EXECUTE_WRITECOPY, 00131 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE | 00132 PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY 00133 }; 00134 00135 00136 00137 ULONG 00138 FASTCALL 00139 MiIsProtectionCompatible ( 00140 IN ULONG OldProtect, 00141 IN ULONG NewProtect 00142 ) 00143 00144 /*++ 00145 00146 Routine Description: 00147 00148 This function takes two user supplied page protections and checks 00149 to see if the new protection is compatible with the old protection. 00150 00151 protection compatible protections 00152 NoAccess NoAccess 00153 ReadOnly NoAccess, ReadOnly, ReadWriteCopy 00154 ReadWriteCopy NoAccess, ReadOnly, ReadWriteCopy 00155 ReadWrite NoAccess, ReadOnly, ReadWriteCopy, ReadWrite 00156 Execute NoAccess, Execute 00157 ExecuteRead NoAccess, ReadOnly, ReadWriteCopy, Execute, ExecuteRead, 00158 ExecuteWriteCopy 00159 ExecuteWrite NoAccess, ReadOnly, ReadWriteCopy, Execute, ExecuteRead, 00160 ExecuteWriteCopy, ReadWrite, ExecuteWrite 00161 ExecuteWriteCopy NoAccess, ReadOnly, ReadWriteCopy, Execute, ExecuteRead, 00162 ExecuteWriteCopy 00163 00164 Arguments: 00165 00166 OldProtect - Supplies the protection to be compatible with. 00167 00168 NewProtect - Supplies the protection to check out. 00169 00170 00171 Return Value: 00172 00173 Returns TRUE if the protection is compatible, FALSE if not. 00174 00175 Environment: 00176 00177 Kernel Mode. 00178 00179 --*/ 00180 00181 { 00182 ULONG Mask; 00183 ULONG ProtectMask; 00184 00185 try { 00186 Mask = MiMakeProtectionMask (OldProtect) & 0x7; 00187 } except (EXCEPTION_EXECUTE_HANDLER) { 00188 return FALSE; 00189 } 00190 00191 ProtectMask = MmCompatibleProtectionMask[Mask] | PAGE_GUARD | PAGE_NOCACHE; 00192 00193 if ((ProtectMask | NewProtect) != ProtectMask) { 00194 return FALSE; 00195 } 00196 return TRUE; 00197 } 00198 00199 00200 // 00201 // Protection data for MiMakeProtectionMask 00202 // 00203 00204 CCHAR MmUserProtectionToMask1[16] = { 00205 0, 00206 MM_NOACCESS, 00207 MM_READONLY, 00208 -1, 00209 MM_READWRITE, 00210 -1, 00211 -1, 00212 -1, 00213 MM_WRITECOPY, 00214 -1, 00215 -1, 00216 -1, 00217 -1, 00218 -1, 00219 -1, 00220 -1 }; 00221 00222 CCHAR MmUserProtectionToMask2[16] = { 00223 0, 00224 MM_EXECUTE, 00225 MM_EXECUTE_READ, 00226 -1, 00227 MM_EXECUTE_READWRITE, 00228 -1, 00229 -1, 00230 -1, 00231 MM_EXECUTE_WRITECOPY, 00232 -1, 00233 -1, 00234 -1, 00235 -1, 00236 -1, 00237 -1, 00238 -1 }; 00239 00240 00241 ULONG 00242 FASTCALL 00243 MiMakeProtectionMask ( 00244 IN ULONG Protect 00245 ) 00246 00247 /*++ 00248 00249 Routine Description: 00250 00251 This function takes a user supplied protection and converts it 00252 into a 5-bit protection code for the PTE. 00253 00254 Arguments: 00255 00256 Protect - Supplies the protection. 00257 00258 00259 Return Value: 00260 00261 Returns the protection code for use in the PTE. 00262 An exception is raised if the user supplied protection is invalid. 00263 00264 Environment: 00265 00266 Kernel Mode. 00267 00268 --*/ 00269 00270 { 00271 ULONG Field1; 00272 ULONG Field2; 00273 ULONG ProtectCode; 00274 00275 if (Protect >= (PAGE_NOCACHE * 2)) { 00276 ExRaiseStatus (STATUS_INVALID_PAGE_PROTECTION); 00277 } 00278 00279 Field1 = Protect & 0xF; 00280 Field2 = (Protect >> 4) & 0xF; 00281 00282 // 00283 // Make sure at least one field is set. 00284 // 00285 00286 if (Field1 == 0) { 00287 if (Field2 == 0) { 00288 00289 // 00290 // Both fields are zero, raise exception. 00291 // 00292 00293 ExRaiseStatus (STATUS_INVALID_PAGE_PROTECTION); 00294 return 0; 00295 } 00296 ProtectCode = MmUserProtectionToMask2[Field2]; 00297 } else { 00298 if (Field2 != 0) { 00299 // 00300 // Both fields are non-zero, raise exception. 00301 // 00302 00303 ExRaiseStatus (STATUS_INVALID_PAGE_PROTECTION); 00304 return 0; 00305 } 00306 ProtectCode = MmUserProtectionToMask1[Field1]; 00307 } 00308 00309 if (ProtectCode == -1) { 00310 ExRaiseStatus (STATUS_INVALID_PAGE_PROTECTION); 00311 } 00312 00313 if (Protect & PAGE_GUARD) { 00314 if (ProtectCode == MM_NOACCESS) { 00315 00316 // 00317 // Invalid protection, no access and no_cache. 00318 // 00319 00320 ExRaiseStatus (STATUS_INVALID_PAGE_PROTECTION); 00321 } 00322 00323 ProtectCode |= MM_GUARD_PAGE; 00324 } 00325 00326 if (Protect & PAGE_NOCACHE) { 00327 00328 if (ProtectCode == MM_NOACCESS) { 00329 00330 // 00331 // Invalid protection, no access and no cache. 00332 // 00333 00334 ExRaiseStatus (STATUS_INVALID_PAGE_PROTECTION); 00335 } 00336 00337 ProtectCode |= MM_NOCACHE; 00338 } 00339 00340 return ProtectCode; 00341 } 00342 00343 #if defined (_WIN64) 00344 00345 LOGICAL 00346 MiDoesPpeExistAndMakeValid ( 00347 IN PMMPTE PointerPpe, 00348 IN PEPROCESS TargetProcess, 00349 IN ULONG PfnMutexHeld, 00350 OUT PULONG Waited 00351 ) 00352 00353 /*++ 00354 00355 Routine Description: 00356 00357 This routine examines the specified Page Directory Parent Entry to determine 00358 if the page directory page mapped by the PPE exists. 00359 00360 If the page directory page exists and is not currently in memory, the 00361 working set mutex and, if held, the PFN mutex are released and the 00362 page directory page is faulted into the working set. The mutexes are 00363 reacquired. 00364 00365 If the PPE exists, the function returns true. 00366 00367 Arguments: 00368 00369 PointerPpe - Supplies a pointer to the PPE to examine and potentially 00370 bring into the working set. 00371 00372 TargetProcess - Supplies a pointer to the current process. 00373 00374 PfnMutexHeld - Supplies the value TRUE if the PFN mutex is held, FALSE 00375 otherwise. 00376 00377 Waited - Supplies a pointer to a ULONG to increment if the mutex is released 00378 and reacquired. Note this value may be incremented more than once. 00379 00380 Return Value: 00381 00382 TRUE if the PPE exists, FALSE if the PPE is zero. 00383 00384 Environment: 00385 00386 Kernel mode, APCs disabled, WorkingSetLock held. 00387 00388 --*/ 00389 00390 { 00391 PMMPTE PointerPde; 00392 KIRQL OldIrql; 00393 00394 OldIrql = APC_LEVEL; 00395 00396 if (PointerPpe->u.Long == 0) { 00397 00398 // 00399 // This page directory parent entry doesn't exist, return FALSE. 00400 // 00401 00402 return FALSE; 00403 } 00404 00405 if (PointerPpe->u.Hard.Valid == 1) { 00406 00407 // 00408 // Already valid. 00409 // 00410 00411 return TRUE; 00412 } 00413 00414 // 00415 // Page directory parent entry exists, it is either valid, in transition 00416 // or in the paging file. Fault it in. 00417 // 00418 00419 if (PfnMutexHeld) { 00420 UNLOCK_PFN (OldIrql); 00421 *Waited += 1; 00422 } 00423 00424 PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe); 00425 00426 *Waited += MiMakeSystemAddressValid (PointerPde, TargetProcess); 00427 00428 if (PfnMutexHeld) { 00429 LOCK_PFN (OldIrql); 00430 } 00431 return TRUE; 00432 } 00433 #endif 00434 00435 00436 ULONG 00437 MiDoesPdeExistAndMakeValid ( 00438 IN PMMPTE PointerPde, 00439 IN PEPROCESS TargetProcess, 00440 IN ULONG PfnMutexHeld, 00441 OUT PULONG Waited 00442 ) 00443 00444 /*++ 00445 00446 Routine Description: 00447 00448 This routine examines the specified Page Directory Entry to determine 00449 if the page table page mapped by the PDE exists. 00450 00451 If the page table page exists and is not currently in memory, the 00452 working set mutex and, if held, the PFN mutex are released and the 00453 page table page is faulted into the working set. The mutexes are 00454 reacquired. 00455 00456 If the PDE exists, the function returns true. 00457 00458 Arguments: 00459 00460 PointerPde - Supplies a pointer to the PDE to examine and potentially 00461 bring into the working set. 00462 00463 TargetProcess - Supplies a pointer to the current process. 00464 00465 PfnMutexHeld - Supplies the value TRUE if the PFN mutex is held, FALSE 00466 otherwise. 00467 00468 Waited - Supplies a pointer to a ULONG to increment if the mutex is released 00469 and reacquired. Note this value may be incremented more than once. 00470 00471 Return Value: 00472 00473 TRUE if the PDE exists, FALSE if the PDE is zero. 00474 00475 Environment: 00476 00477 Kernel mode, APCs disabled, WorkingSetLock held. 00478 00479 --*/ 00480 00481 { 00482 PMMPTE PointerPte; 00483 KIRQL OldIrql; 00484 00485 OldIrql = APC_LEVEL; 00486 00487 if (PointerPde->u.Long == 0) { 00488 00489 // 00490 // This page directory entry doesn't exist, return FALSE. 00491 // 00492 00493 return FALSE; 00494 } 00495 00496 if (PointerPde->u.Hard.Valid == 1) { 00497 00498 // 00499 // Already valid. 00500 // 00501 00502 return TRUE; 00503 } 00504 00505 // 00506 // Page directory entry exists, it is either valid, in transition 00507 // or in the paging file. Fault it in. 00508 // 00509 00510 if (PfnMutexHeld) { 00511 UNLOCK_PFN (OldIrql); 00512 *Waited += 1; 00513 } 00514 00515 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde); 00516 00517 *Waited += MiMakeSystemAddressValid (PointerPte, TargetProcess); 00518 00519 if (PfnMutexHeld) { 00520 LOCK_PFN (OldIrql); 00521 } 00522 return TRUE; 00523 } 00524 00525 #if defined (_WIN64) 00526 00527 ULONG 00528 MiMakePpeExistAndMakeValid ( 00529 IN PMMPTE PointerPpe, 00530 IN PEPROCESS TargetProcess, 00531 IN ULONG PfnMutexHeld 00532 ) 00533 00534 /*++ 00535 00536 Routine Description: 00537 00538 This routine examines the specified Page Directory Parent Entry to 00539 determine if the page directory page mapped by the PPE exists. 00540 00541 If the page directory page exists and is not currently in memory, the 00542 working set mutex and, if held, the PFN mutex are released and the 00543 page directory page is faulted into the working set. The mutexes are 00544 reacquired. 00545 00546 If the PPE exists, the function returns true. 00547 00548 If the PPE does not exist, a zero filled page directory is created 00549 and it is brought into the working set. In this case the return 00550 value is FALSE. 00551 00552 Arguments: 00553 00554 PointerPpe - Supplies a pointer to the PPE to examine and bring 00555 into the working set. 00556 00557 TargetProcess - Supplies a pointer to the current process. 00558 00559 PfnMutexHeld - Supplies the value TRUE if the PFN mutex is held, FALSE 00560 otherwise. 00561 00562 Return Value: 00563 00564 TRUE if the PDE exists, FALSE if the PPE was created. 00565 00566 Environment: 00567 00568 Kernel mode, APCs disabled, WorkingSetLock held. 00569 00570 --*/ 00571 00572 { 00573 PMMPTE PointerPde; 00574 KIRQL OldIrql; 00575 ULONG ReturnValue; 00576 00577 if (PointerPpe->u.Hard.Valid == 1) { 00578 00579 // 00580 // Already valid. 00581 // 00582 00583 return TRUE; 00584 } 00585 00586 // 00587 // Deal with the page directory page first. 00588 // 00589 00590 if (PointerPpe->u.Long == 0) { 00591 ReturnValue = FALSE; 00592 } else { 00593 ReturnValue = TRUE; 00594 } 00595 00596 // 00597 // Page directory parent entry not valid, make it valid. 00598 // 00599 00600 OldIrql = APC_LEVEL; 00601 00602 if (PfnMutexHeld) { 00603 UNLOCK_PFN (OldIrql); 00604 } 00605 00606 // 00607 // Fault it in. 00608 // 00609 00610 PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe); 00611 MiMakeSystemAddressValid (PointerPde, TargetProcess); 00612 00613 ASSERT (PointerPpe->u.Hard.Valid == 1); 00614 00615 if (PfnMutexHeld) { 00616 LOCK_PFN (OldIrql); 00617 } 00618 00619 return ReturnValue; 00620 } 00621 #endif 00622 00623 ULONG 00624 MiMakePdeExistAndMakeValid ( 00625 IN PMMPTE PointerPde, 00626 IN PEPROCESS TargetProcess, 00627 IN ULONG PfnMutexHeld 00628 ) 00629 00630 /*++ 00631 00632 Routine Description: 00633 00634 This routine examines the specified Page Directory Parent Entry to 00635 determine if the page directory page mapped by the PPE exists. If it does, 00636 then it examines the specified Page Directory Entry to determine if 00637 the page table page mapped by the PDE exists. 00638 00639 If the page table page exists and is not currently in memory, the 00640 working set mutex and, if held, the PFN mutex are released and the 00641 page table page is faulted into the working set. The mutexes are 00642 reacquired. 00643 00644 If the PDE exists, the function returns true. 00645 00646 If the PDE does not exist, a zero filled PTE is created and it 00647 too is brought into the working set. In this case the return 00648 value is FALSE. 00649 00650 Arguments: 00651 00652 PointerPde - Supplies a pointer to the PDE to examine and bring 00653 into the working set. 00654 00655 TargetProcess - Supplies a pointer to the current process. 00656 00657 PfnMutexHeld - Supplies the value TRUE if the PFN mutex is held, FALSE 00658 otherwise. 00659 00660 Return Value: 00661 00662 TRUE if the PDE exists, FALSE if the PDE was created. 00663 00664 Environment: 00665 00666 Kernel mode, APCs disabled, WorkingSetLock held. 00667 00668 --*/ 00669 00670 { 00671 PMMPTE PointerPte; 00672 PMMPTE PointerPpe; 00673 KIRQL OldIrql; 00674 ULONG ReturnValue; 00675 00676 PointerPpe = MiGetPteAddress (PointerPde); 00677 00678 if (PointerPpe->u.Hard.Valid == 1 && PointerPde->u.Hard.Valid == 1) { 00679 00680 // 00681 // Already valid. 00682 // 00683 00684 return TRUE; 00685 } 00686 00687 // 00688 // Deal with the page directory page first. 00689 // 00690 00691 if (PointerPpe->u.Long == 0) { 00692 ReturnValue = FALSE; 00693 } else { 00694 ReturnValue = TRUE; 00695 } 00696 00697 // 00698 // Page directory parent entry not valid, make it valid. 00699 // 00700 00701 OldIrql = APC_LEVEL; 00702 00703 do { 00704 00705 if (PfnMutexHeld) { 00706 UNLOCK_PFN (OldIrql); 00707 } 00708 00709 // 00710 // Fault it in. 00711 // 00712 00713 MiMakeSystemAddressValid (PointerPde, TargetProcess); 00714 00715 ASSERT (PointerPpe->u.Hard.Valid == 1); 00716 00717 if (PfnMutexHeld) { 00718 LOCK_PFN (OldIrql); 00719 } 00720 00721 // 00722 // Now deal with the page table page. 00723 // 00724 00725 if (ReturnValue == TRUE) { 00726 if (PointerPde->u.Long == 0) { 00727 ReturnValue = FALSE; 00728 } else { 00729 ReturnValue = TRUE; 00730 } 00731 } 00732 00733 // 00734 // Page directory entry not valid, make it valid. 00735 // 00736 00737 OldIrql = APC_LEVEL; 00738 00739 if (PfnMutexHeld) { 00740 UNLOCK_PFN (OldIrql); 00741 } 00742 00743 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde); 00744 00745 // 00746 // Fault it in. 00747 // 00748 00749 MiMakeSystemAddressValid (PointerPte, TargetProcess); 00750 00751 ASSERT (PointerPde->u.Hard.Valid == 1); 00752 00753 if (PfnMutexHeld) { 00754 LOCK_PFN (OldIrql); 00755 } 00756 00757 } while (PointerPpe->u.Hard.Valid == 0 || PointerPde->u.Hard.Valid == 0); 00758 00759 return ReturnValue; 00760 } 00761 00762 ULONG 00763 FASTCALL 00764 MiMakeSystemAddressValid ( 00765 IN PVOID VirtualAddress, 00766 IN PEPROCESS CurrentProcess 00767 ) 00768 00769 /*++ 00770 00771 Routine Description: 00772 00773 This routine checks to see if the virtual address is valid, and if 00774 not makes it valid. 00775 00776 Arguments: 00777 00778 VirtualAddress - Supplies the virtual address to make valid. 00779 00780 CurrentProcess - Supplies a pointer to the current process. 00781 00782 Return Value: 00783 00784 Returns TRUE if lock released and wait performed, FALSE otherwise. 00785 00786 Environment: 00787 00788 Kernel mode, APCs disabled, WorkingSetLock held. 00789 00790 --*/ 00791 00792 { 00793 NTSTATUS status; 00794 LOGICAL WsHeldSafe; 00795 ULONG Waited; 00796 00797 Waited = FALSE; 00798 00799 ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS); 00800 00801 ASSERT ((VirtualAddress < MM_PAGED_POOL_START) || 00802 (VirtualAddress > MmPagedPoolEnd)); 00803 00804 while (!MmIsAddressValid(VirtualAddress)) { 00805 00806 // 00807 // The virtual address is not present. Release 00808 // the working set mutex and fault it in. 00809 // 00810 // The working set lock may have been acquired safely or unsafely 00811 // by our caller. Handle both cases here and below. 00812 // 00813 00814 UNLOCK_WS_REGARDLESS(CurrentProcess, WsHeldSafe); 00815 00816 status = MmAccessFault (FALSE, VirtualAddress, KernelMode, (PVOID)0); 00817 if (!NT_SUCCESS(status)) { 00818 KdPrint (("MM:page fault status %lx\n",status)); 00819 KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR, 00820 1, 00821 (ULONG)status, 00822 (ULONG_PTR)CurrentProcess, 00823 (ULONG_PTR)VirtualAddress); 00824 } 00825 00826 LOCK_WS_REGARDLESS(CurrentProcess, WsHeldSafe); 00827 00828 Waited = TRUE; 00829 } 00830 00831 return Waited; 00832 } 00833 00834 00835 ULONG 00836 FASTCALL 00837 MiMakeSystemAddressValidPfnWs ( 00838 IN PVOID VirtualAddress, 00839 IN PEPROCESS CurrentProcess OPTIONAL 00840 ) 00841 00842 /*++ 00843 00844 Routine Description: 00845 00846 This routine checks to see if the virtual address is valid, and if 00847 not makes it valid. 00848 00849 Arguments: 00850 00851 VirtualAddress - Supplies the virtual address to make valid. 00852 00853 CurrentProcess - Supplies a pointer to the current process, if the 00854 working set lock is not held, this value is NULL. 00855 00856 Return Value: 00857 00858 Returns TRUE if lock released and wait performed, FALSE otherwise. 00859 00860 Environment: 00861 00862 Kernel mode, APCs disabled, PFN lock held, working set lock held 00863 if CurrentProcess != NULL. 00864 00865 --*/ 00866 00867 { 00868 NTSTATUS status; 00869 ULONG Waited; 00870 KIRQL OldIrql; 00871 LOGICAL WsHeldSafe; 00872 00873 Waited = FALSE; 00874 OldIrql = APC_LEVEL; 00875 00876 ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS); 00877 00878 while (!MmIsAddressValid(VirtualAddress)) { 00879 00880 // 00881 // The virtual address is not present. Release 00882 // the working set mutex and fault it in. 00883 // 00884 00885 UNLOCK_PFN (OldIrql); 00886 if (CurrentProcess != NULL) { 00887 00888 // 00889 // The working set lock may have been acquired safely or unsafely 00890 // by our caller. Handle both cases here and below. 00891 // 00892 00893 UNLOCK_WS_REGARDLESS(CurrentProcess, WsHeldSafe); 00894 } 00895 status = MmAccessFault (FALSE, VirtualAddress, KernelMode, (PVOID)0); 00896 if (!NT_SUCCESS(status)) { 00897 KdPrint (("MM:page fault status %lx\n",status)); 00898 KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR, 00899 2, 00900 (ULONG)status, 00901 (ULONG_PTR)CurrentProcess, 00902 (ULONG_PTR)VirtualAddress); 00903 } 00904 if (CurrentProcess != NULL) { 00905 LOCK_WS_REGARDLESS(CurrentProcess, WsHeldSafe); 00906 } 00907 LOCK_PFN (OldIrql); 00908 00909 Waited = TRUE; 00910 } 00911 return Waited; 00912 } 00913 00914 ULONG 00915 FASTCALL 00916 MiMakeSystemAddressValidPfnSystemWs ( 00917 IN PVOID VirtualAddress 00918 ) 00919 00920 /*++ 00921 00922 Routine Description: 00923 00924 This routine checks to see if the virtual address is valid, and if 00925 not makes it valid. 00926 00927 Arguments: 00928 00929 VirtualAddress - Supplies the virtual address to make valid. 00930 00931 Return Value: 00932 00933 Returns TRUE if lock released and wait performed, FALSE otherwise. 00934 00935 Environment: 00936 00937 Kernel mode, APCs disabled, PFN lock held, system working set lock held. 00938 00939 --*/ 00940 00941 { 00942 NTSTATUS status; 00943 ULONG Waited; 00944 KIRQL OldIrql; 00945 KIRQL OldIrqlWs; 00946 LOGICAL SessionSpace; 00947 00948 Waited = FALSE; 00949 OldIrql = APC_LEVEL; 00950 OldIrqlWs = APC_LEVEL; 00951 00952 ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS); 00953 00954 SessionSpace = MI_IS_SESSION_IMAGE_ADDRESS (VirtualAddress); 00955 00956 while (!MmIsAddressValid(VirtualAddress)) { 00957 00958 // 00959 // The virtual address is not present. Release 00960 // the working set mutex and fault it in. 00961 // 00962 00963 UNLOCK_PFN (OldIrql); 00964 00965 if (SessionSpace == TRUE) { 00966 UNLOCK_SESSION_SPACE_WS (OldIrqlWs); 00967 } 00968 else { 00969 UNLOCK_SYSTEM_WS (OldIrqlWs); 00970 } 00971 00972 status = MmAccessFault (FALSE, VirtualAddress, KernelMode, (PVOID)0); 00973 if (!NT_SUCCESS(status)) { 00974 KdPrint (("MM:page fault status %lx\n",status)); 00975 KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR, 00976 2, 00977 (ULONG)status, 00978 (ULONG_PTR)0, 00979 (ULONG_PTR)VirtualAddress); 00980 } 00981 if (SessionSpace == TRUE) { 00982 LOCK_SESSION_SPACE_WS (OldIrqlWs); 00983 } 00984 else { 00985 LOCK_SYSTEM_WS (OldIrqlWs); 00986 } 00987 LOCK_PFN (OldIrql); 00988 00989 Waited = TRUE; 00990 } 00991 return Waited; 00992 } 00993 00994 ULONG 00995 FASTCALL 00996 MiMakeSystemAddressValidPfn ( 00997 IN PVOID VirtualAddress 00998 ) 00999 01000 /*++ 01001 01002 Routine Description: 01003 01004 This routine checks to see if the virtual address is valid, and if 01005 not makes it valid. 01006 01007 Arguments: 01008 01009 VirtualAddress - Supplies the virtual address to make valid. 01010 01011 Return Value: 01012 01013 Returns TRUE if lock released and wait performed, FALSE otherwise. 01014 01015 Environment: 01016 01017 Kernel mode, APCs disabled, only the PFN Lock held. 01018 01019 --*/ 01020 01021 { 01022 NTSTATUS status; 01023 KIRQL OldIrql = APC_LEVEL; 01024 01025 ULONG Waited = FALSE; 01026 01027 ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS); 01028 01029 while (!MmIsAddressValid(VirtualAddress)) { 01030 01031 // 01032 // The virtual address is not present. Release 01033 // the working set mutex and fault it in. 01034 // 01035 01036 UNLOCK_PFN (OldIrql); 01037 01038 status = MmAccessFault (FALSE, VirtualAddress, KernelMode, (PVOID)0); 01039 if (!NT_SUCCESS(status)) { 01040 KdPrint (("MM:page fault status %lx\n",status)); 01041 KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR, 01042 3, 01043 (ULONG)status, 01044 (ULONG_PTR)VirtualAddress, 01045 0); 01046 } 01047 01048 LOCK_PFN (OldIrql); 01049 01050 Waited = TRUE; 01051 } 01052 01053 return Waited; 01054 } 01055 01056 ULONG 01057 FASTCALL 01058 MiLockPagedAddress ( 01059 IN PVOID VirtualAddress, 01060 IN ULONG PfnLockHeld 01061 ) 01062 01063 /*++ 01064 01065 Routine Description: 01066 01067 This routine checks to see if the virtual address is valid, and if 01068 not makes it valid. 01069 01070 Arguments: 01071 01072 VirtualAddress - Supplies the virtual address to make valid. 01073 01074 CurrentProcess - Supplies a pointer to the current process. 01075 01076 Return Value: 01077 01078 Returns TRUE if lock released and wait performed, FALSE otherwise. 01079 01080 Environment: 01081 01082 Kernel mode. 01083 01084 --*/ 01085 01086 { 01087 01088 PMMPTE PointerPte; 01089 PMMPFN Pfn1; 01090 KIRQL OldIrql; 01091 ULONG Waited = FALSE; 01092 01093 PointerPte = MiGetPteAddress(VirtualAddress); 01094 01095 // 01096 // The address must be within paged pool. 01097 // 01098 01099 if (PfnLockHeld == FALSE) { 01100 LOCK_PFN2 (OldIrql); 01101 } 01102 01103 if (PointerPte->u.Hard.Valid == 0) { 01104 01105 Waited = MiMakeSystemAddressValidPfn ( 01106 MiGetVirtualAddressMappedByPte(PointerPte)); 01107 01108 } 01109 01110 Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber); 01111 MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 6); 01112 Pfn1->u3.e2.ReferenceCount += 1; 01113 01114 if (PfnLockHeld == FALSE) { 01115 UNLOCK_PFN2 (OldIrql); 01116 } 01117 return Waited; 01118 } 01119 01120 01121 VOID 01122 FASTCALL 01123 MiUnlockPagedAddress ( 01124 IN PVOID VirtualAddress, 01125 IN ULONG PfnLockHeld 01126 ) 01127 01128 /*++ 01129 01130 Routine Description: 01131 01132 This routine checks to see if the virtual address is valid, and if 01133 not makes it valid. 01134 01135 Arguments: 01136 01137 VirtualAddress - Supplies the virtual address to make valid. 01138 01139 01140 Return Value: 01141 01142 None. 01143 01144 Environment: 01145 01146 Kernel mode. PFN LOCK MUST NOT BE HELD. 01147 01148 --*/ 01149 01150 { 01151 PMMPFN Pfn1; 01152 PMMPTE PointerPte; 01153 KIRQL OldIrql; 01154 PFN_NUMBER PageFrameIndex; 01155 01156 PointerPte = MiGetPteAddress(VirtualAddress); 01157 01158 // 01159 // Address must be within paged pool. 01160 // 01161 01162 if (PfnLockHeld == FALSE) { 01163 LOCK_PFN2 (OldIrql); 01164 } 01165 01166 ASSERT (PointerPte->u.Hard.Valid == 1); 01167 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte); 01168 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 01169 01170 ASSERT (Pfn1->u3.e2.ReferenceCount > 1); 01171 01172 MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn1, 7); 01173 01174 MiDecrementReferenceCount (PageFrameIndex); 01175 01176 if (PfnLockHeld == FALSE) { 01177 UNLOCK_PFN2 (OldIrql); 01178 } 01179 return; 01180 } 01181 01182 VOID 01183 FASTCALL 01184 MiZeroPhysicalPage ( 01185 IN PFN_NUMBER PageFrameIndex, 01186 IN ULONG PageColor 01187 ) 01188 01189 /*++ 01190 01191 Routine Description: 01192 01193 This procedure maps the specified physical page into hyper space 01194 and fills the page with zeros. 01195 01196 Arguments: 01197 01198 PageFrameIndex - Supplies the physical page number to fill with 01199 zeroes. 01200 01201 Return Value: 01202 01203 none. 01204 01205 Environment: 01206 01207 Kernel mode. 01208 01209 --*/ 01210 01211 { 01212 PULONG va; 01213 KIRQL OldIrql; 01214 01215 #if defined(_ALPHA_) 01216 01217 HalZeroPage((PVOID)ULongToPtr((PageColor & MM_COLOR_MASK) << PAGE_SHIFT), 01218 (PVOID)ULongToPtr((PageColor & MM_COLOR_MASK) << PAGE_SHIFT), 01219 PageFrameIndex); 01220 #else 01221 01222 UNREFERENCED_PARAMETER (PageColor); 01223 01224 va = (PULONG)MiMapPageInHyperSpace (PageFrameIndex, &OldIrql); 01225 01226 #if defined(_X86_) 01227 01228 KeZeroPage(va); 01229 01230 #else 01231 01232 RtlZeroMemory (va, PAGE_SIZE); 01233 01234 #endif // X86 01235 01236 MiUnmapPageInHyperSpace (OldIrql); 01237 01238 #endif // ALPHA 01239 01240 return; 01241 } 01242 01243 VOID 01244 FASTCALL 01245 MiRestoreTransitionPte ( 01246 IN PFN_NUMBER PageFrameIndex 01247 ) 01248 01249 /*++ 01250 01251 Routine Description: 01252 01253 This procedure restores the original contents into the PTE (which could 01254 be a prototype PTE) referred to by the PFN database for the specified 01255 physical page. It also updates all necessary data structures to 01256 reflect the fact that the referenced PTE is no longer in transition. 01257 01258 The physical address of the referenced PTE is mapped into hyper space 01259 of the current process and the PTE is then updated. 01260 01261 Arguments: 01262 01263 PageFrameIndex - Supplies the physical page number which refers to a 01264 transition PTE. 01265 01266 Return Value: 01267 01268 none. 01269 01270 Environment: 01271 01272 Must be holding the PFN database mutex with APCs disabled. 01273 01274 --*/ 01275 01276 { 01277 PMMPFN Pfn1; 01278 PMMPTE PointerPte; 01279 PSUBSECTION Subsection; 01280 PCONTROL_AREA ControlArea; 01281 KIRQL OldIrql = 99; 01282 01283 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 01284 01285 ASSERT (Pfn1->u3.e1.PageLocation == StandbyPageList); 01286 01287 if (Pfn1->u3.e1.PrototypePte) { 01288 01289 if (MmIsAddressValid (Pfn1->PteAddress)) { 01290 PointerPte = Pfn1->PteAddress; 01291 } else { 01292 01293 // 01294 // The page containing the prototype PTE is not valid, 01295 // map the page into hyperspace and reference it that way. 01296 // 01297 01298 PointerPte = MiMapPageInHyperSpace (Pfn1->PteFrame, &OldIrql); 01299 PointerPte = (PMMPTE)((PCHAR)PointerPte + 01300 MiGetByteOffset(Pfn1->PteAddress)); 01301 } 01302 01303 ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == PageFrameIndex) && 01304 (PointerPte->u.Hard.Valid == 0)); 01305 01306 // 01307 // This page is referenced by a prototype PTE. The 01308 // segment structures need to be updated when the page 01309 // is removed from the transition state. 01310 // 01311 01312 if (Pfn1->OriginalPte.u.Soft.Prototype) { 01313 01314 // 01315 // The prototype PTE is in subsection format, calculate the 01316 // address of the control area for the subsection and decrement 01317 // the number of PFN references to the control area. 01318 // 01319 // Calculate address of subsection for this prototype PTE. 01320 // 01321 01322 Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte); 01323 ControlArea = Subsection->ControlArea; 01324 ControlArea->NumberOfPfnReferences -= 1; 01325 ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0); 01326 01327 MiCheckForControlAreaDeletion (ControlArea); 01328 } 01329 01330 } else { 01331 01332 // 01333 // The page points to a page or page table page which may not be 01334 // for the current process. Map the page into hyperspace and 01335 // reference it through hyperspace. If the page resides in 01336 // system space (but not session space), it does not need to be 01337 // mapped as all PTEs for system space must be resident. Session 01338 // space PTEs are only mapped per session so access to them must 01339 // also go through hyperspace. 01340 // 01341 01342 PointerPte = Pfn1->PteAddress; 01343 01344 if (PointerPte < MiGetPteAddress (MM_SYSTEM_SPACE_START) || 01345 MI_IS_SESSION_PTE (PointerPte)) { 01346 01347 PointerPte = MiMapPageInHyperSpace (Pfn1->PteFrame, &OldIrql); 01348 PointerPte = (PMMPTE)((PCHAR)PointerPte + 01349 MiGetByteOffset(Pfn1->PteAddress)); 01350 } 01351 ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == PageFrameIndex) && 01352 (PointerPte->u.Hard.Valid == 0)); 01353 01354 MI_CAPTURE_USED_PAGETABLE_ENTRIES (Pfn1); 01355 01356 #if _WIN64 01357 #if DBGXX 01358 MiCheckPageTableTrim(PointerPte); 01359 #endif 01360 #endif 01361 } 01362 01363 ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0); 01364 ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) && 01365 (Pfn1->OriginalPte.u.Soft.Transition == 1))); 01366 01367 MI_WRITE_INVALID_PTE (PointerPte, Pfn1->OriginalPte); 01368 01369 if (OldIrql != 99) { 01370 MiUnmapPageInHyperSpace (OldIrql); 01371 } 01372 01373 // 01374 // The PTE has been restored to its original contents and is 01375 // no longer in transition. Decrement the share count on 01376 // the page table page which contains the PTE. 01377 // 01378 01379 MiDecrementShareCount (Pfn1->PteFrame); 01380 01381 return; 01382 } 01383 01384 PSUBSECTION 01385 MiGetSubsectionAndProtoFromPte ( 01386 IN PMMPTE PointerPte, 01387 OUT PMMPTE *ProtoPte, 01388 IN PEPROCESS Process 01389 ) 01390 01391 /*++ 01392 01393 Routine Description: 01394 01395 This routine examines the contents of the supplied PTE (which must 01396 map a page within a section) and determines the address of the 01397 subsection in which the PTE is contained. 01398 01399 Arguments: 01400 01401 PointerPte - Supplies a pointer to the PTE. 01402 01403 ProtoPte - Supplies a pointer to a PMMPTE which receives the 01404 address of the prototype PTE which is mapped by the supplied 01405 PointerPte. 01406 01407 Process - Supplies a pointer to the current process. 01408 01409 Return Value: 01410 01411 Returns the pointer to the subsection for this PTE. 01412 01413 Environment: 01414 01415 Kernel mode - Must be holding the PFN database lock and 01416 working set mutex (acquired safely) with APCs disabled. 01417 01418 --*/ 01419 01420 { 01421 PMMPTE PointerProto; 01422 PMMPFN Pfn1; 01423 01424 if (PointerPte->u.Hard.Valid == 1) { 01425 Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber); 01426 *ProtoPte = Pfn1->PteAddress; 01427 return MiGetSubsectionAddress (&Pfn1->OriginalPte); 01428 } 01429 01430 PointerProto = MiPteToProto (PointerPte); 01431 *ProtoPte = PointerProto; 01432 01433 MiMakeSystemAddressValidPfnWs (PointerProto, Process); 01434 01435 if (PointerProto->u.Hard.Valid == 1) { 01436 // 01437 // Prototype Pte is valid. 01438 // 01439 01440 Pfn1 = MI_PFN_ELEMENT (PointerProto->u.Hard.PageFrameNumber); 01441 return MiGetSubsectionAddress (&Pfn1->OriginalPte); 01442 } 01443 01444 if ((PointerProto->u.Soft.Transition == 1) && 01445 (PointerProto->u.Soft.Prototype == 0)) { 01446 01447 // 01448 // Prototype Pte is in transition. 01449 // 01450 01451 Pfn1 = MI_PFN_ELEMENT (PointerProto->u.Trans.PageFrameNumber); 01452 return MiGetSubsectionAddress (&Pfn1->OriginalPte); 01453 } 01454 01455 ASSERT (PointerProto->u.Soft.Prototype == 1); 01456 return MiGetSubsectionAddress (PointerProto); 01457 } 01458 01459 BOOLEAN 01460 MmIsNonPagedSystemAddressValid ( 01461 IN PVOID VirtualAddress 01462 ) 01463 01464 /*++ 01465 01466 Routine Description: 01467 01468 For a given virtual address this function returns TRUE if the address 01469 is within the nonpagable portion of the system's address space, 01470 FALSE otherwise. 01471 01472 Arguments: 01473 01474 VirtualAddress - Supplies the virtual address to check. 01475 01476 Return Value: 01477 01478 TRUE if the address is within the nonpagable portion of the system 01479 address space, FALSE otherwise. 01480 01481 Environment: 01482 01483 Kernel mode. 01484 01485 --*/ 01486 01487 { 01488 // 01489 // Return TRUE if address is within the nonpagable portion 01490 // of the system. Check limits for paged pool and if not within 01491 // those limits, return TRUE. 01492 // 01493 01494 if ((VirtualAddress >= MmPagedPoolStart) && 01495 (VirtualAddress <= MmPagedPoolEnd)) { 01496 return FALSE; 01497 } 01498 01499 return TRUE; 01500 } 01501 01502 VOID 01503 MmHibernateInformation ( 01504 IN PVOID MemoryMap, 01505 OUT PULONG_PTR HiberVa, 01506 OUT PPHYSICAL_ADDRESS HiberPte 01507 ) 01508 { 01509 // 01510 // Mark PTE page where the 16 dump PTEs reside as needing cloned 01511 // 01512 01513 PoSetHiberRange ( 01514 MemoryMap, 01515 PO_MEM_CLONE, 01516 MmCrashDumpPte, 01517 1, 01518 ' etP' 01519 ); 01520 01521 // 01522 // Return the dump PTEs to the loader (as it needs to use them 01523 // to map it's relocation code into the kernel space on the 01524 // final bit of restoring memory) 01525 // 01526 01527 *HiberVa = (ULONG_PTR) MiGetVirtualAddressMappedByPte(MmCrashDumpPte); 01528 *HiberPte = MmGetPhysicalAddress(MmCrashDumpPte); 01529 } 01530 01531 #if _WIN64 01532 #if DBGXX 01533 01534 ULONG zok[16]; 01535 01536 VOID 01537 MiCheckPageTableTrim( 01538 IN PMMPTE PointerPte 01539 ) 01540 { 01541 ULONG i; 01542 PFN_NUMBER Frame; 01543 PMMPFN Pfn; 01544 PMMPTE FrameData; 01545 PMMPTE p; 01546 ULONG count; 01547 01548 Frame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte); 01549 Pfn = MI_PFN_ELEMENT (Frame); 01550 01551 if (Pfn->UsedPageTableEntries) { 01552 01553 count = 0; 01554 01555 p = FrameData = (PMMPTE)KSEG_ADDRESS (Frame); 01556 01557 for (i = 0; i < PTE_PER_PAGE; i += 1, p += 1) { 01558 if (p->u.Long != 0) { 01559 count += 1; 01560 } 01561 } 01562 01563 DbgPrint ("MiCheckPageTableTrim: %I64X %I64X %I64X\n", 01564 PointerPte, Pfn, Pfn->UsedPageTableEntries); 01565 01566 if (count != Pfn->UsedPageTableEntries) { 01567 DbgPrint ("MiCheckPageTableTrim1: %I64X %I64X %I64X %I64X\n", 01568 PointerPte, Pfn, Pfn->UsedPageTableEntries, count); 01569 DbgBreakPoint(); 01570 } 01571 zok[0] += 1; 01572 } 01573 else { 01574 zok[1] += 1; 01575 } 01576 } 01577 01578 VOID 01579 MiCheckPageTableInPage( 01580 IN PMMPFN Pfn, 01581 IN PMMINPAGE_SUPPORT Support 01582 ) 01583 { 01584 ULONG i; 01585 PFN_NUMBER Frame; 01586 PMMPTE FrameData; 01587 PMMPTE p; 01588 ULONG count; 01589 01590 if (Support->UsedPageTableEntries) { 01591 01592 Frame = (PFN_NUMBER)((PMMPFN)Pfn - (PMMPFN)MmPfnDatabase); 01593 01594 count = 0; 01595 01596 p = FrameData = (PMMPTE)KSEG_ADDRESS (Frame); 01597 01598 for (i = 0; i < PTE_PER_PAGE; i += 1, p += 1) { 01599 if (p->u.Long != 0) { 01600 count += 1; 01601 } 01602 } 01603 01604 DbgPrint ("MiCheckPageTableIn: %I64X %I64X %I64X\n", 01605 FrameData, Pfn, Support->UsedPageTableEntries); 01606 01607 if (count != Support->UsedPageTableEntries) { 01608 DbgPrint ("MiCheckPageTableIn1: %I64X %I64X %I64X %I64X\n", 01609 FrameData, Pfn, Support->UsedPageTableEntries, count); 01610 DbgBreakPoint(); 01611 } 01612 zok[2] += 1; 01613 } 01614 else { 01615 zok[3] += 1; 01616 } 01617 } 01618 #endif 01619 #endif

Generated on Sat May 15 19:40:50 2004 for test by doxygen 1.3.7