Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1480 lines
34 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. mmsup.c
  5. Abstract:
  6. This module contains the various routines for miscellaneous support
  7. operations for memory management.
  8. Author:
  9. Lou Perazzoli (loup) 31-Aug-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(PAGE, MmHibernateInformation)
  16. #pragma alloc_text(PAGE, MiMakeSystemAddressValid)
  17. #pragma alloc_text(PAGE, MiIsPteDecommittedPage)
  18. #endif
  19. #if defined (_WIN64)
  20. #if DBGXX
  21. VOID
  22. MiCheckPageTableTrim(
  23. IN PMMPTE PointerPte
  24. );
  25. #endif
  26. #endif
  27. ULONG
  28. FASTCALL
  29. MiIsPteDecommittedPage (
  30. IN PMMPTE PointerPte
  31. )
  32. /*++
  33. Routine Description:
  34. This function checks the contents of a PTE to determine if the
  35. PTE is explicitly decommitted.
  36. If the PTE is a prototype PTE and the protection is not in the
  37. prototype PTE, the value FALSE is returned.
  38. Arguments:
  39. PointerPte - Supplies a pointer to the PTE to examine.
  40. Return Value:
  41. TRUE if the PTE is in the explicit decommitted state.
  42. FALSE if the PTE is not in the explicit decommitted state.
  43. Environment:
  44. Kernel mode, APCs disabled, working set mutex held.
  45. --*/
  46. {
  47. MMPTE PteContents;
  48. PteContents = *PointerPte;
  49. //
  50. // If the protection in the PTE is not decommitted, return FALSE.
  51. //
  52. if (PteContents.u.Soft.Protection != MM_DECOMMIT) {
  53. return FALSE;
  54. }
  55. //
  56. // Check to make sure the protection field is really being interpreted
  57. // correctly.
  58. //
  59. if (PteContents.u.Hard.Valid == 1) {
  60. //
  61. // The PTE is valid and therefore cannot be decommitted.
  62. //
  63. return FALSE;
  64. }
  65. if ((PteContents.u.Soft.Prototype == 1) &&
  66. (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)) {
  67. //
  68. // The PTE's protection is not known as it is in
  69. // prototype PTE format. Return FALSE.
  70. //
  71. return FALSE;
  72. }
  73. //
  74. // It is a decommitted PTE.
  75. //
  76. return TRUE;
  77. }
  78. //
  79. // Data for is protection compatible.
  80. //
  81. ULONG MmCompatibleProtectionMask[8] = {
  82. PAGE_NOACCESS,
  83. PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
  84. PAGE_NOACCESS | PAGE_EXECUTE,
  85. PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
  86. PAGE_EXECUTE_READ,
  87. PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE,
  88. PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
  89. PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE |
  90. PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE |
  91. PAGE_EXECUTE_WRITECOPY,
  92. PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
  93. PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
  94. };
  95. ULONG
  96. FASTCALL
  97. MiIsProtectionCompatible (
  98. IN ULONG OldProtect,
  99. IN ULONG NewProtect
  100. )
  101. /*++
  102. Routine Description:
  103. This function takes two user supplied page protections and checks
  104. to see if the new protection is compatible with the old protection.
  105. protection compatible protections
  106. NoAccess NoAccess
  107. ReadOnly NoAccess, ReadOnly, ReadWriteCopy
  108. ReadWriteCopy NoAccess, ReadOnly, ReadWriteCopy
  109. ReadWrite NoAccess, ReadOnly, ReadWriteCopy, ReadWrite
  110. Execute NoAccess, Execute
  111. ExecuteRead NoAccess, ReadOnly, ReadWriteCopy, Execute, ExecuteRead,
  112. ExecuteWriteCopy
  113. ExecuteWrite NoAccess, ReadOnly, ReadWriteCopy, Execute, ExecuteRead,
  114. ExecuteWriteCopy, ReadWrite, ExecuteWrite
  115. ExecuteWriteCopy NoAccess, ReadOnly, ReadWriteCopy, Execute, ExecuteRead,
  116. ExecuteWriteCopy
  117. Arguments:
  118. OldProtect - Supplies the protection to be compatible with.
  119. NewProtect - Supplies the protection to check out.
  120. Return Value:
  121. Returns TRUE if the protection is compatible, FALSE if not.
  122. Environment:
  123. Kernel Mode.
  124. --*/
  125. {
  126. ULONG Mask;
  127. ULONG ProtectMask;
  128. ULONG PteProtection;
  129. PteProtection = MiMakeProtectionMask (OldProtect);
  130. if (PteProtection == MM_INVALID_PROTECTION) {
  131. return FALSE;
  132. }
  133. Mask = PteProtection & 0x7;
  134. ProtectMask = MmCompatibleProtectionMask[Mask] | PAGE_GUARD | PAGE_NOCACHE;
  135. if ((ProtectMask | NewProtect) != ProtectMask) {
  136. return FALSE;
  137. }
  138. return TRUE;
  139. }
  140. ULONG
  141. FASTCALL
  142. MiIsPteProtectionCompatible (
  143. IN ULONG PteProtection,
  144. IN ULONG NewProtect
  145. )
  146. {
  147. ULONG Mask;
  148. ULONG ProtectMask;
  149. Mask = PteProtection & 0x7;
  150. ProtectMask = MmCompatibleProtectionMask[Mask] | PAGE_GUARD | PAGE_NOCACHE;
  151. if ((ProtectMask | NewProtect) != ProtectMask) {
  152. return FALSE;
  153. }
  154. return TRUE;
  155. }
  156. //
  157. // Protection data for MiMakeProtectionMask
  158. //
  159. CCHAR MmUserProtectionToMask1[16] = {
  160. 0,
  161. MM_NOACCESS,
  162. MM_READONLY,
  163. -1,
  164. MM_READWRITE,
  165. -1,
  166. -1,
  167. -1,
  168. MM_WRITECOPY,
  169. -1,
  170. -1,
  171. -1,
  172. -1,
  173. -1,
  174. -1,
  175. -1 };
  176. CCHAR MmUserProtectionToMask2[16] = {
  177. 0,
  178. MM_EXECUTE,
  179. MM_EXECUTE_READ,
  180. -1,
  181. MM_EXECUTE_READWRITE,
  182. -1,
  183. -1,
  184. -1,
  185. MM_EXECUTE_WRITECOPY,
  186. -1,
  187. -1,
  188. -1,
  189. -1,
  190. -1,
  191. -1,
  192. -1 };
  193. ULONG
  194. FASTCALL
  195. MiMakeProtectionMask (
  196. IN ULONG Protect
  197. )
  198. /*++
  199. Routine Description:
  200. This function takes a user supplied protection and converts it
  201. into a 5-bit protection code for the PTE.
  202. Arguments:
  203. Protect - Supplies the protection.
  204. Return Value:
  205. Returns the protection code for use in the PTE. Note that
  206. MM_INVALID_PROTECTION (-1) is returned for an invalid protection
  207. request. Since valid PTE protections fit in 5 bits and are
  208. zero-extended, it's easy for callers to distinguish this.
  209. Environment:
  210. Kernel Mode.
  211. --*/
  212. {
  213. ULONG Field1;
  214. ULONG Field2;
  215. ULONG ProtectCode;
  216. if (Protect >= (PAGE_NOCACHE * 2)) {
  217. return MM_INVALID_PROTECTION;
  218. }
  219. Field1 = Protect & 0xF;
  220. Field2 = (Protect >> 4) & 0xF;
  221. //
  222. // Make sure at least one field is set.
  223. //
  224. if (Field1 == 0) {
  225. if (Field2 == 0) {
  226. //
  227. // Both fields are zero, return failure.
  228. //
  229. return MM_INVALID_PROTECTION;
  230. }
  231. ProtectCode = MmUserProtectionToMask2[Field2];
  232. } else {
  233. if (Field2 != 0) {
  234. //
  235. // Both fields are non-zero, raise failure.
  236. //
  237. return MM_INVALID_PROTECTION;
  238. }
  239. ProtectCode = MmUserProtectionToMask1[Field1];
  240. }
  241. if (ProtectCode == -1) {
  242. return MM_INVALID_PROTECTION;
  243. }
  244. if (Protect & PAGE_GUARD) {
  245. if (ProtectCode == MM_NOACCESS) {
  246. //
  247. // Invalid protection, no access and no_cache.
  248. //
  249. return MM_INVALID_PROTECTION;
  250. }
  251. ProtectCode |= MM_GUARD_PAGE;
  252. }
  253. if (Protect & PAGE_NOCACHE) {
  254. if (ProtectCode == MM_NOACCESS) {
  255. //
  256. // Invalid protection, no access and no cache.
  257. //
  258. return MM_INVALID_PROTECTION;
  259. }
  260. ProtectCode |= MM_NOCACHE;
  261. }
  262. return ProtectCode;
  263. }
  264. ULONG
  265. MiDoesPdeExistAndMakeValid (
  266. IN PMMPTE PointerPde,
  267. IN PEPROCESS TargetProcess,
  268. IN KIRQL OldIrql,
  269. OUT PULONG Waited
  270. )
  271. /*++
  272. Routine Description:
  273. This routine examines the specified Page Directory Entry to determine
  274. if the page table page mapped by the PDE exists.
  275. If the page table page exists and is not currently in memory, the
  276. working set mutex and, if held, the PFN lock are released and the
  277. page table page is faulted into the working set. The mutexes are
  278. reacquired.
  279. If the PDE exists, the function returns TRUE.
  280. Arguments:
  281. PointerPde - Supplies a pointer to the PDE to examine and potentially
  282. bring into the working set.
  283. TargetProcess - Supplies a pointer to the current process.
  284. OldIrql - Supplies the IRQL the caller acquired the PFN lock at or MM_NOIRQL
  285. if the caller does not hold the PFN lock.
  286. Waited - Supplies a pointer to a ULONG to increment if the mutex is released
  287. and reacquired. Note this value may be incremented more than once.
  288. Return Value:
  289. TRUE if the PDE exists, FALSE if the PDE is zero.
  290. Environment:
  291. Kernel mode, APCs disabled, working set mutex held.
  292. --*/
  293. {
  294. PMMPTE PointerPte;
  295. ASSERT (KeAreAllApcsDisabled () == TRUE);
  296. if (PointerPde->u.Long == 0) {
  297. //
  298. // This page directory entry doesn't exist, return FALSE.
  299. //
  300. return FALSE;
  301. }
  302. if (PointerPde->u.Hard.Valid == 1) {
  303. //
  304. // Already valid.
  305. //
  306. return TRUE;
  307. }
  308. //
  309. // Page directory entry exists, it is either valid, in transition
  310. // or in the paging file. Fault it in.
  311. //
  312. if (OldIrql != MM_NOIRQL) {
  313. UNLOCK_PFN (OldIrql);
  314. ASSERT (KeAreAllApcsDisabled () == TRUE);
  315. *Waited += 1;
  316. }
  317. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  318. *Waited += MiMakeSystemAddressValid (PointerPte, TargetProcess);
  319. if (OldIrql != MM_NOIRQL) {
  320. LOCK_PFN (OldIrql);
  321. }
  322. return TRUE;
  323. }
  324. VOID
  325. MiMakePdeExistAndMakeValid (
  326. IN PMMPTE PointerPde,
  327. IN PEPROCESS TargetProcess,
  328. IN KIRQL OldIrql
  329. )
  330. /*++
  331. Routine Description:
  332. This routine examines the specified Page Directory Parent Entry to
  333. determine if the page directory page mapped by the PPE exists. If it does,
  334. then it examines the specified Page Directory Entry to determine if
  335. the page table page mapped by the PDE exists.
  336. If the page table page exists and is not currently in memory, the
  337. working set mutex and, if held, the PFN lock are released and the
  338. page table page is faulted into the working set. The mutexes are
  339. reacquired.
  340. If the PDE does not exist, a zero filled PTE is created and it
  341. too is brought into the working set.
  342. Arguments:
  343. PointerPde - Supplies a pointer to the PDE to examine and bring
  344. into the working set.
  345. TargetProcess - Supplies a pointer to the current process.
  346. OldIrql - Supplies the IRQL the caller acquired the PFN lock at or MM_NOIRQL
  347. if the caller does not hold the PFN lock.
  348. Return Value:
  349. None.
  350. Environment:
  351. Kernel mode, APCs disabled, working set mutex held.
  352. --*/
  353. {
  354. PMMPTE PointerPte;
  355. PMMPTE PointerPpe;
  356. PMMPTE PointerPxe;
  357. ASSERT (KeAreAllApcsDisabled () == TRUE);
  358. PointerPpe = MiGetPteAddress (PointerPde);
  359. PointerPxe = MiGetPdeAddress (PointerPde);
  360. if ((PointerPxe->u.Hard.Valid == 1) &&
  361. (PointerPpe->u.Hard.Valid == 1) &&
  362. (PointerPde->u.Hard.Valid == 1)) {
  363. //
  364. // Already valid.
  365. //
  366. return;
  367. }
  368. //
  369. // Page directory parent (or extended parent) entry not valid,
  370. // make it valid.
  371. //
  372. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  373. do {
  374. if (OldIrql != MM_NOIRQL) {
  375. UNLOCK_PFN (OldIrql);
  376. }
  377. ASSERT (KeAreAllApcsDisabled () == TRUE);
  378. //
  379. // Fault it in.
  380. //
  381. MiMakeSystemAddressValid (PointerPte, TargetProcess);
  382. ASSERT (PointerPxe->u.Hard.Valid == 1);
  383. ASSERT (PointerPpe->u.Hard.Valid == 1);
  384. ASSERT (PointerPde->u.Hard.Valid == 1);
  385. if (OldIrql != MM_NOIRQL) {
  386. LOCK_PFN (OldIrql);
  387. }
  388. } while ((PointerPxe->u.Hard.Valid == 0) ||
  389. (PointerPpe->u.Hard.Valid == 0) ||
  390. (PointerPde->u.Hard.Valid == 0));
  391. return;
  392. }
  393. ULONG
  394. FASTCALL
  395. MiMakeSystemAddressValid (
  396. IN PVOID VirtualAddress,
  397. IN PEPROCESS CurrentProcess
  398. )
  399. /*++
  400. Routine Description:
  401. This routine checks to see if the virtual address is valid, and if
  402. not makes it valid.
  403. Arguments:
  404. VirtualAddress - Supplies the virtual address to make valid.
  405. CurrentProcess - Supplies a pointer to the current process.
  406. Return Value:
  407. Returns TRUE if the working set mutex was released and wait performed,
  408. FALSE otherwise.
  409. Environment:
  410. Kernel mode, APCs disabled, working set mutex held.
  411. --*/
  412. {
  413. NTSTATUS status;
  414. LOGICAL WsHeldSafe;
  415. ULONG Waited;
  416. Waited = FALSE;
  417. ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS);
  418. ASSERT ((VirtualAddress < MM_PAGED_POOL_START) ||
  419. (VirtualAddress > MmPagedPoolEnd));
  420. ASSERT (KeAreAllApcsDisabled () == TRUE);
  421. while (!MmIsAddressValid (VirtualAddress)) {
  422. //
  423. // The virtual address is not present. Release
  424. // the working set mutex and fault it in.
  425. //
  426. // The working set mutex may have been acquired safely or unsafely
  427. // by our caller. Handle both cases here and below.
  428. //
  429. UNLOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
  430. status = MmAccessFault (FALSE, VirtualAddress, KernelMode, NULL);
  431. if (!NT_SUCCESS(status)) {
  432. KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR,
  433. 1,
  434. (ULONG)status,
  435. (ULONG_PTR)CurrentProcess,
  436. (ULONG_PTR)VirtualAddress);
  437. }
  438. LOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
  439. Waited = TRUE;
  440. }
  441. return Waited;
  442. }
  443. ULONG
  444. FASTCALL
  445. MiMakeSystemAddressValidPfnWs (
  446. IN PVOID VirtualAddress,
  447. IN PEPROCESS CurrentProcess OPTIONAL,
  448. IN KIRQL OldIrql
  449. )
  450. /*++
  451. Routine Description:
  452. This routine checks to see if the virtual address is valid, and if
  453. not makes it valid.
  454. Arguments:
  455. VirtualAddress - Supplies the virtual address to make valid.
  456. CurrentProcess - Supplies a pointer to the current process, if the
  457. working set mutex is not held, this value is NULL.
  458. OldIrql - Supplies the IRQL the caller acquired the PFN lock.
  459. Return Value:
  460. Returns TRUE if lock/mutex released and wait performed, FALSE otherwise.
  461. Environment:
  462. Kernel mode, APCs disabled, PFN lock held, working set mutex held
  463. if CurrentProcess != NULL.
  464. --*/
  465. {
  466. NTSTATUS status;
  467. ULONG Waited;
  468. LOGICAL WsHeldSafe;
  469. ASSERT (OldIrql != MM_NOIRQL);
  470. Waited = FALSE;
  471. //
  472. // Initializing WsHeldSafe is not needed for correctness, but without it
  473. // the compiler cannot compile this code W4 to check for use of
  474. // uninitialized variables.
  475. //
  476. WsHeldSafe = FALSE;
  477. ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS);
  478. while (!MiIsAddressValid (VirtualAddress, TRUE)) {
  479. //
  480. // The virtual address is not present. Release
  481. // the working set mutex and fault it in.
  482. //
  483. UNLOCK_PFN (OldIrql);
  484. if (CurrentProcess != NULL) {
  485. //
  486. // The working set mutex may have been acquired safely or unsafely
  487. // by our caller. Handle both cases here and below.
  488. //
  489. UNLOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
  490. }
  491. status = MmAccessFault (FALSE, VirtualAddress, KernelMode, NULL);
  492. if (!NT_SUCCESS(status)) {
  493. KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR,
  494. 2,
  495. (ULONG)status,
  496. (ULONG_PTR)CurrentProcess,
  497. (ULONG_PTR)VirtualAddress);
  498. }
  499. if (CurrentProcess != NULL) {
  500. LOCK_WS_REGARDLESS (CurrentProcess, WsHeldSafe);
  501. }
  502. LOCK_PFN (OldIrql);
  503. Waited = TRUE;
  504. }
  505. return Waited;
  506. }
  507. ULONG
  508. FASTCALL
  509. MiMakeSystemAddressValidPfnSystemWs (
  510. IN PVOID VirtualAddress,
  511. IN KIRQL OldIrql
  512. )
  513. /*++
  514. Routine Description:
  515. This routine checks to see if the virtual address is valid, and if
  516. not makes it valid.
  517. Arguments:
  518. VirtualAddress - Supplies the virtual address to make valid.
  519. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  520. Return Value:
  521. Returns TRUE if lock/mutex released and wait performed, FALSE otherwise.
  522. Environment:
  523. Kernel mode, APCs disabled, PFN lock held, system working set mutex held.
  524. --*/
  525. {
  526. PMMSUPPORT Ws;
  527. NTSTATUS status;
  528. ASSERT (OldIrql != MM_NOIRQL);
  529. ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS);
  530. if (MiIsAddressValid (VirtualAddress, FALSE)) {
  531. return FALSE;
  532. }
  533. if (MI_IS_SESSION_IMAGE_ADDRESS (VirtualAddress)) {
  534. Ws = &MmSessionSpace->GlobalVirtualAddress->Vm;
  535. }
  536. else {
  537. Ws = &MmSystemCacheWs;
  538. }
  539. do {
  540. //
  541. // The virtual address is not present. Release
  542. // the working set mutex and fault it in.
  543. //
  544. UNLOCK_PFN (OldIrql);
  545. UNLOCK_WORKING_SET (Ws);
  546. status = MmAccessFault (FALSE, VirtualAddress, KernelMode, NULL);
  547. if (!NT_SUCCESS(status)) {
  548. KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR,
  549. 2,
  550. (ULONG)status,
  551. (ULONG_PTR)0,
  552. (ULONG_PTR)VirtualAddress);
  553. }
  554. LOCK_WORKING_SET (Ws);
  555. LOCK_PFN (OldIrql);
  556. } while (!MmIsAddressValid (VirtualAddress));
  557. return TRUE;
  558. }
  559. ULONG
  560. FASTCALL
  561. MiMakeSystemAddressValidPfn (
  562. IN PVOID VirtualAddress,
  563. IN KIRQL OldIrql
  564. )
  565. /*++
  566. Routine Description:
  567. This routine checks to see if the virtual address is valid, and if
  568. not makes it valid.
  569. Arguments:
  570. VirtualAddress - Supplies the virtual address to make valid.
  571. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  572. Return Value:
  573. Returns TRUE if lock released and wait performed, FALSE otherwise.
  574. Environment:
  575. Kernel mode, APCs disabled, only the PFN lock held.
  576. --*/
  577. {
  578. NTSTATUS status;
  579. ULONG Waited = FALSE;
  580. ASSERT (VirtualAddress > MM_HIGHEST_USER_ADDRESS);
  581. while (!MiIsAddressValid (VirtualAddress, TRUE)) {
  582. //
  583. // The virtual address is not present. Release
  584. // the PFN lock and fault it in.
  585. //
  586. UNLOCK_PFN (OldIrql);
  587. status = MmAccessFault (FALSE, VirtualAddress, KernelMode, NULL);
  588. if (!NT_SUCCESS(status)) {
  589. KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR,
  590. 3,
  591. (ULONG)status,
  592. (ULONG_PTR)VirtualAddress,
  593. 0);
  594. }
  595. LOCK_PFN (OldIrql);
  596. Waited = TRUE;
  597. }
  598. return Waited;
  599. }
  600. VOID
  601. FASTCALL
  602. MiLockPagedAddress (
  603. IN PVOID VirtualAddress
  604. )
  605. /*++
  606. Routine Description:
  607. This routine checks to see if the virtual address is valid, and if
  608. not makes it valid.
  609. Arguments:
  610. VirtualAddress - Supplies the virtual address to make valid.
  611. Return Value:
  612. Returns TRUE if lock released and wait performed, FALSE otherwise.
  613. Environment:
  614. Kernel mode.
  615. --*/
  616. {
  617. KIRQL OldIrql;
  618. PMMPFN Pfn1;
  619. PMMPTE PointerPte;
  620. PointerPte = MiGetPteAddress (VirtualAddress);
  621. //
  622. // The address must be within paged pool.
  623. //
  624. LOCK_PFN (OldIrql);
  625. if (PointerPte->u.Hard.Valid == 0) {
  626. MiMakeSystemAddressValidPfn (VirtualAddress, OldIrql);
  627. }
  628. Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
  629. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, TRUE, 6);
  630. Pfn1->u3.e2.ReferenceCount += 1;
  631. UNLOCK_PFN (OldIrql);
  632. return;
  633. }
  634. VOID
  635. FASTCALL
  636. MiUnlockPagedAddress (
  637. IN PVOID VirtualAddress,
  638. IN ULONG PfnLockHeld
  639. )
  640. /*++
  641. Routine Description:
  642. This routine checks to see if the virtual address is valid, and if
  643. not makes it valid.
  644. Arguments:
  645. VirtualAddress - Supplies the virtual address to make valid.
  646. Return Value:
  647. None.
  648. Environment:
  649. Kernel mode. PFN LOCK MUST NOT BE HELD.
  650. --*/
  651. {
  652. PMMPFN Pfn1;
  653. PMMPTE PointerPte;
  654. KIRQL OldIrql;
  655. PFN_NUMBER PageFrameIndex;
  656. PointerPte = MiGetPteAddress(VirtualAddress);
  657. //
  658. // Initializing OldIrql is not needed for correctness, but without it
  659. // the compiler cannot compile this code W4 to check for use of
  660. // uninitialized variables.
  661. //
  662. OldIrql = PASSIVE_LEVEL;
  663. //
  664. // Address must be within paged pool.
  665. //
  666. if (PfnLockHeld == FALSE) {
  667. LOCK_PFN2 (OldIrql);
  668. }
  669. ASSERT (PointerPte->u.Hard.Valid == 1);
  670. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  671. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  672. ASSERT (Pfn1->u3.e2.ReferenceCount > 1);
  673. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(Pfn1, 7);
  674. if (PfnLockHeld == FALSE) {
  675. UNLOCK_PFN2 (OldIrql);
  676. }
  677. return;
  678. }
  679. VOID
  680. FASTCALL
  681. MiZeroPhysicalPage (
  682. IN PFN_NUMBER PageFrameIndex,
  683. IN ULONG PageColor
  684. )
  685. /*++
  686. Routine Description:
  687. This procedure maps the specified physical page into hyper space
  688. and fills the page with zeros.
  689. Arguments:
  690. PageFrameIndex - Supplies the physical page number to fill with zeroes.
  691. Return Value:
  692. None.
  693. Environment:
  694. Kernel mode.
  695. --*/
  696. {
  697. KIRQL OldIrql;
  698. PVOID VirtualAddress;
  699. PEPROCESS Process;
  700. UNREFERENCED_PARAMETER (PageColor);
  701. Process = PsGetCurrentProcess ();
  702. VirtualAddress = MiMapPageInHyperSpace (Process, PageFrameIndex, &OldIrql);
  703. KeZeroPages (VirtualAddress, PAGE_SIZE);
  704. MiUnmapPageInHyperSpace (Process, VirtualAddress, OldIrql);
  705. return;
  706. }
  707. VOID
  708. FASTCALL
  709. MiRestoreTransitionPte (
  710. IN PMMPFN Pfn1
  711. )
  712. /*++
  713. Routine Description:
  714. This procedure restores the original contents into the PTE (which could
  715. be a prototype PTE) referred to by the PFN database for the specified
  716. physical page. It also updates all necessary data structures to
  717. reflect the fact that the referenced PTE is no longer in transition.
  718. The physical address of the referenced PTE is mapped into hyper space
  719. of the current process and the PTE is then updated.
  720. Arguments:
  721. Pfn1 - Supplies the PFN element which refers to a transition PTE.
  722. Return Value:
  723. none.
  724. Environment:
  725. Must be holding the PFN lock.
  726. --*/
  727. {
  728. PMMPFN Pfn2;
  729. PMMPTE PointerPte;
  730. PSUBSECTION Subsection;
  731. PCONTROL_AREA ControlArea;
  732. PEPROCESS Process;
  733. PFN_NUMBER PageTableFrameIndex;
  734. Process = NULL;
  735. ASSERT (Pfn1->u3.e1.PageLocation == StandbyPageList);
  736. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  737. if (Pfn1->u3.e1.PrototypePte) {
  738. if (MiIsAddressValid (Pfn1->PteAddress, TRUE)) {
  739. PointerPte = Pfn1->PteAddress;
  740. } else {
  741. //
  742. // The page containing the prototype PTE is not valid,
  743. // map the page into hyperspace and reference it that way.
  744. //
  745. Process = PsGetCurrentProcess ();
  746. PointerPte = MiMapPageInHyperSpaceAtDpc (Process, Pfn1->u4.PteFrame);
  747. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  748. MiGetByteOffset(Pfn1->PteAddress));
  749. }
  750. ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == MI_PFN_ELEMENT_TO_INDEX (Pfn1)) &&
  751. (PointerPte->u.Hard.Valid == 0));
  752. //
  753. // This page is referenced by a prototype PTE. The
  754. // segment structures need to be updated when the page
  755. // is removed from the transition state.
  756. //
  757. if (Pfn1->OriginalPte.u.Soft.Prototype) {
  758. //
  759. // The prototype PTE is in subsection format, calculate the
  760. // address of the control area for the subsection and decrement
  761. // the number of PFN references to the control area.
  762. //
  763. // Calculate address of subsection for this prototype PTE.
  764. //
  765. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  766. ControlArea = Subsection->ControlArea;
  767. ControlArea->NumberOfPfnReferences -= 1;
  768. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  769. MiCheckForControlAreaDeletion (ControlArea);
  770. }
  771. } else {
  772. //
  773. // The page points to a page or page table page which may not be
  774. // for the current process. Map the page into hyperspace and
  775. // reference it through hyperspace. If the page resides in
  776. // system space (but not session space), it does not need to be
  777. // mapped as all PTEs for system space must be resident. Session
  778. // space PTEs are only mapped per session so access to them must
  779. // also go through hyperspace.
  780. //
  781. PointerPte = Pfn1->PteAddress;
  782. if (PointerPte < MiGetPteAddress ((PVOID)MM_SYSTEM_SPACE_START) ||
  783. MI_IS_SESSION_PTE (PointerPte)) {
  784. Process = PsGetCurrentProcess ();
  785. PointerPte = MiMapPageInHyperSpaceAtDpc (Process, Pfn1->u4.PteFrame);
  786. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  787. MiGetByteOffset(Pfn1->PteAddress));
  788. }
  789. ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == MI_PFN_ELEMENT_TO_INDEX (Pfn1)) &&
  790. (PointerPte->u.Hard.Valid == 0));
  791. MI_CAPTURE_USED_PAGETABLE_ENTRIES (Pfn1);
  792. #if defined (_WIN64)
  793. #if DBGXX
  794. MiCheckPageTableTrim(PointerPte);
  795. #endif
  796. #endif
  797. }
  798. ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0);
  799. ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  800. (Pfn1->OriginalPte.u.Soft.Transition == 1)));
  801. MI_WRITE_INVALID_PTE (PointerPte, Pfn1->OriginalPte);
  802. if (Process != NULL) {
  803. MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
  804. }
  805. Pfn1->u3.e1.CacheAttribute = MiNotMapped;
  806. //
  807. // The PTE has been restored to its original contents and is
  808. // no longer in transition. Decrement the share count on
  809. // the page table page which contains the PTE.
  810. //
  811. PageTableFrameIndex = Pfn1->u4.PteFrame;
  812. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  813. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  814. return;
  815. }
  816. PSUBSECTION
  817. MiGetSubsectionAndProtoFromPte (
  818. IN PMMPTE PointerPte,
  819. OUT PMMPTE *ProtoPte
  820. )
  821. /*++
  822. Routine Description:
  823. This routine examines the contents of the supplied PTE (which must
  824. map a page within a section) and determines the address of the
  825. subsection in which the PTE is contained.
  826. Arguments:
  827. PointerPte - Supplies a pointer to the PTE.
  828. ProtoPte - Supplies a pointer to a PMMPTE which receives the
  829. address of the prototype PTE which is mapped by the supplied
  830. PointerPte.
  831. Return Value:
  832. Returns the pointer to the subsection for this PTE.
  833. Environment:
  834. Kernel mode - Must be holding the PFN lock and
  835. working set mutex (acquired safely) with APCs disabled.
  836. --*/
  837. {
  838. PMMPTE PointerProto;
  839. PMMPFN Pfn1;
  840. KIRQL OldIrql;
  841. PSUBSECTION Subsection;
  842. LOCK_PFN (OldIrql);
  843. if (PointerPte->u.Hard.Valid == 1) {
  844. Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
  845. *ProtoPte = Pfn1->PteAddress;
  846. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  847. UNLOCK_PFN (OldIrql);
  848. return Subsection;
  849. }
  850. PointerProto = MiPteToProto (PointerPte);
  851. *ProtoPte = PointerProto;
  852. if (MiGetPteAddress (PointerProto)->u.Hard.Valid == 0) {
  853. MiMakeSystemAddressValidPfn (PointerProto, OldIrql);
  854. }
  855. if (PointerProto->u.Hard.Valid == 1) {
  856. //
  857. // Prototype PTE is valid.
  858. //
  859. Pfn1 = MI_PFN_ELEMENT (PointerProto->u.Hard.PageFrameNumber);
  860. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  861. UNLOCK_PFN (OldIrql);
  862. return Subsection;
  863. }
  864. if ((PointerProto->u.Soft.Transition == 1) &&
  865. (PointerProto->u.Soft.Prototype == 0)) {
  866. //
  867. // Prototype PTE is in transition.
  868. //
  869. Pfn1 = MI_PFN_ELEMENT (PointerProto->u.Trans.PageFrameNumber);
  870. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  871. UNLOCK_PFN (OldIrql);
  872. return Subsection;
  873. }
  874. ASSERT (PointerProto->u.Soft.Prototype == 1);
  875. Subsection = MiGetSubsectionAddress (PointerProto);
  876. UNLOCK_PFN (OldIrql);
  877. return Subsection;
  878. }
  879. BOOLEAN
  880. MmIsNonPagedSystemAddressValid (
  881. IN PVOID VirtualAddress
  882. )
  883. /*++
  884. Routine Description:
  885. For a given virtual address this function returns TRUE if the address
  886. is within the nonpagable portion of the system's address space,
  887. FALSE otherwise.
  888. Arguments:
  889. VirtualAddress - Supplies the virtual address to check.
  890. Return Value:
  891. TRUE if the address is within the nonpagable portion of the system
  892. address space, FALSE otherwise.
  893. Environment:
  894. Kernel mode.
  895. --*/
  896. {
  897. //
  898. // Return TRUE if address is within the nonpagable portion
  899. // of the system. Check limits for paged pool and if not within
  900. // those limits, return TRUE.
  901. //
  902. if ((VirtualAddress >= MmPagedPoolStart) &&
  903. (VirtualAddress <= MmPagedPoolEnd)) {
  904. return FALSE;
  905. }
  906. //
  907. // Check special pool before checking session space because on NT64
  908. // nonpaged session pool exists in session space (on NT32, nonpaged
  909. // session requests are satisfied from systemwide nonpaged pool instead).
  910. //
  911. if (MmIsSpecialPoolAddress (VirtualAddress)) {
  912. if (MiIsSpecialPoolAddressNonPaged (VirtualAddress)) {
  913. return TRUE;
  914. }
  915. return FALSE;
  916. }
  917. if ((VirtualAddress >= (PVOID) MmSessionBase) &&
  918. (VirtualAddress < (PVOID) MiSessionSpaceEnd)) {
  919. return FALSE;
  920. }
  921. return TRUE;
  922. }
  923. VOID
  924. MmHibernateInformation (
  925. IN PVOID MemoryMap,
  926. OUT PULONG_PTR HiberVa,
  927. OUT PPHYSICAL_ADDRESS HiberPte
  928. )
  929. {
  930. //
  931. // Mark PTE page where the 16 dump PTEs reside as needing cloning.
  932. //
  933. PoSetHiberRange (MemoryMap, PO_MEM_CLONE, MmCrashDumpPte, 1, ' etP');
  934. //
  935. // Return the dump PTEs to the loader (as it needs to use them
  936. // to map it's relocation code into the kernel space on the
  937. // final bit of restoring memory).
  938. //
  939. *HiberVa = (ULONG_PTR) MiGetVirtualAddressMappedByPte(MmCrashDumpPte);
  940. *HiberPte = MmGetPhysicalAddress(MmCrashDumpPte);
  941. }
  942. #if defined (_WIN64)
  943. PVOID
  944. MmGetMaxWowAddress (
  945. VOID
  946. )
  947. /*++
  948. Routine Description:
  949. This function returns the WOW usermode address boundary.
  950. Arguments:
  951. None.
  952. Return Value:
  953. The highest Wow usermode address boundary.
  954. Environment:
  955. The calling process must be the relevant wow64 process as each process
  956. can have a different limit (based on its PE header, etc).
  957. --*/
  958. {
  959. if (PsGetCurrentProcess()->Wow64Process == NULL) {
  960. return NULL;
  961. }
  962. ASSERT (MmWorkingSetList->HighestUserAddress != NULL);
  963. return MmWorkingSetList->HighestUserAddress;
  964. }
  965. #if DBGXX
  966. ULONG zok[16];
  967. VOID
  968. MiCheckPageTableTrim(
  969. IN PMMPTE PointerPte
  970. )
  971. {
  972. ULONG i;
  973. PFN_NUMBER Frame;
  974. PMMPFN Pfn;
  975. PMMPTE FrameData;
  976. PMMPTE p;
  977. ULONG count;
  978. Frame = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
  979. Pfn = MI_PFN_ELEMENT (Frame);
  980. if (Pfn->UsedPageTableEntries) {
  981. count = 0;
  982. p = FrameData = (PMMPTE)KSEG_ADDRESS (Frame);
  983. for (i = 0; i < PTE_PER_PAGE; i += 1, p += 1) {
  984. if (p->u.Long != 0) {
  985. count += 1;
  986. }
  987. }
  988. DbgPrint ("MiCheckPageTableTrim: %I64X %I64X %I64X\n",
  989. PointerPte, Pfn, Pfn->UsedPageTableEntries);
  990. if (count != Pfn->UsedPageTableEntries) {
  991. DbgPrint ("MiCheckPageTableTrim1: %I64X %I64X %I64X %I64X\n",
  992. PointerPte, Pfn, Pfn->UsedPageTableEntries, count);
  993. DbgBreakPoint();
  994. }
  995. zok[0] += 1;
  996. }
  997. else {
  998. zok[1] += 1;
  999. }
  1000. }
  1001. VOID
  1002. MiCheckPageTableInPage(
  1003. IN PMMPFN Pfn,
  1004. IN PMMINPAGE_SUPPORT Support
  1005. )
  1006. {
  1007. ULONG i;
  1008. PFN_NUMBER Frame;
  1009. PMMPTE FrameData;
  1010. PMMPTE p;
  1011. ULONG count;
  1012. if (Support->UsedPageTableEntries) {
  1013. Frame = (PFN_NUMBER)((PMMPFN)Pfn - (PMMPFN)MmPfnDatabase);
  1014. count = 0;
  1015. p = FrameData = (PMMPTE)KSEG_ADDRESS (Frame);
  1016. for (i = 0; i < PTE_PER_PAGE; i += 1, p += 1) {
  1017. if (p->u.Long != 0) {
  1018. count += 1;
  1019. }
  1020. }
  1021. DbgPrint ("MiCheckPageTableIn: %I64X %I64X %I64X\n",
  1022. FrameData, Pfn, Support->UsedPageTableEntries);
  1023. if (count != Support->UsedPageTableEntries) {
  1024. DbgPrint ("MiCheckPageTableIn1: %I64X %I64X %I64X %I64X\n",
  1025. FrameData, Pfn, Support->UsedPageTableEntries, count);
  1026. DbgBreakPoint();
  1027. }
  1028. zok[2] += 1;
  1029. }
  1030. else {
  1031. zok[3] += 1;
  1032. }
  1033. }
  1034. #endif
  1035. #endif