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.

6548 lines
196 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. pagfault.c
  5. Abstract:
  6. This module contains the pager for memory management.
  7. Author:
  8. Lou Perazzoli (loup) 10-Apr-1989
  9. Landy Wang (landyw) 02-June-1997
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. #if defined ( _WIN64)
  14. #if DBGXX
  15. VOID
  16. MiCheckPageTableInPage (
  17. IN PMMPFN Pfn,
  18. IN PMMINPAGE_SUPPORT Support
  19. );
  20. #endif
  21. #endif
  22. #define STATUS_PTE_CHANGED 0x87303000
  23. #define STATUS_REFAULT 0xC7303001
  24. ULONG MmInPageSupportMinimum = 4;
  25. ULONG MiInPageSinglePages;
  26. extern PMMPTE MmSharedUserDataPte;
  27. extern PVOID MmSpecialPoolStart;
  28. extern PVOID MmSpecialPoolEnd;
  29. ULONG MiFaultRetries;
  30. ULONG MiUserFaultRetries;
  31. ULONG MmClusterPageFileReads;
  32. #define MI_PROTOTYPE_WSINDEX ((ULONG)-1)
  33. NTSTATUS
  34. MiResolvePageFileFault (
  35. IN PVOID FaultingAddress,
  36. IN PMMPTE PointerPte,
  37. IN PMMINPAGE_SUPPORT *ReadBlock,
  38. IN PEPROCESS Process,
  39. IN KIRQL OldIrql
  40. );
  41. NTSTATUS
  42. MiResolveProtoPteFault (
  43. IN ULONG_PTR StoreInstruction,
  44. IN PVOID VirtualAddress,
  45. IN PMMPTE PointerPte,
  46. IN PMMPTE PointerProtoPte,
  47. IN OUT PMMPFN *LockedProtoPfn,
  48. IN PMMINPAGE_SUPPORT *ReadBlock,
  49. IN PEPROCESS Process,
  50. IN KIRQL OldIrql,
  51. OUT PLOGICAL ApcNeeded
  52. );
  53. VOID
  54. MiHandleBankedSection (
  55. IN PVOID VirtualAddress,
  56. IN PMMVAD Vad
  57. );
  58. NTSTATUS
  59. MiResolveMappedFileFault (
  60. IN PVOID FaultingAddress,
  61. IN PMMPTE PointerPte,
  62. IN PMMINPAGE_SUPPORT *ReadBlock,
  63. IN PEPROCESS Process,
  64. IN KIRQL OldIrql
  65. );
  66. NTSTATUS
  67. MiResolveTransitionFault (
  68. IN PVOID FaultingAddress,
  69. IN PMMPTE PointerPte,
  70. IN PEPROCESS Process,
  71. IN KIRQL OldIrql,
  72. OUT PLOGICAL ApcNeeded,
  73. OUT PMMINPAGE_SUPPORT *InPageBlock
  74. );
  75. NTSTATUS
  76. MiCompleteProtoPteFault (
  77. IN ULONG_PTR StoreInstruction,
  78. IN PVOID FaultingAddress,
  79. IN PMMPTE PointerPte,
  80. IN PMMPTE PointerProtoPte,
  81. IN KIRQL OldIrql,
  82. IN OUT PMMPFN *LockedProtoPfn
  83. );
  84. ULONG MmMaxTransitionCluster = 8;
  85. NTSTATUS
  86. MiDispatchFault (
  87. IN ULONG_PTR FaultStatus,
  88. IN PVOID VirtualAddress,
  89. IN PMMPTE PointerPte,
  90. IN PMMPTE PointerProtoPte,
  91. IN LOGICAL RecheckAccess,
  92. IN PEPROCESS Process,
  93. IN PMMVAD Vad,
  94. OUT PLOGICAL ApcNeeded
  95. )
  96. /*++
  97. Routine Description:
  98. This routine dispatches a page fault to the appropriate
  99. routine to complete the fault.
  100. Arguments:
  101. FaultStatus - Supplies fault status information bits.
  102. VirtualAddress - Supplies the faulting address.
  103. PointerPte - Supplies the PTE for the faulting address.
  104. PointerProtoPte - Supplies a pointer to the prototype PTE to fault in,
  105. NULL if no prototype PTE exists.
  106. RecheckAccess - Supplies TRUE if the prototype PTE needs to be checked for
  107. access permissions - this is only an issue for forked
  108. prototype PTEs that are no-access.
  109. Process - Supplies a pointer to the process object. If this
  110. parameter is NULL, then the fault is for system
  111. space and the process's working set lock is not held.
  112. If this parameter is HYDRA_PROCESS, then the fault is for session
  113. space and the process's working set lock is not held - rather
  114. the session space's working set lock is held.
  115. Vad - Supplies the VAD used for sections. May optionally be NULL even
  116. for section-based faults, so use purely as an opportunistic hint.
  117. ApcNeeded - Supplies a pointer to a location set to TRUE if an I/O
  118. completion APC is needed to complete partial IRPs that
  119. collided.
  120. It is the caller's responsibility to initialize this (usually
  121. to FALSE) on entry. However, since this routine may be called
  122. multiple times for a single fault (for the page directory,
  123. page table and the page itself), it is possible for it to
  124. occasionally be TRUE on entry.
  125. If it is FALSE on exit, no completion APC is needed.
  126. Return Value:
  127. NTSTATUS.
  128. Environment:
  129. Kernel mode, working set mutex held.
  130. --*/
  131. {
  132. #if DBG
  133. KIRQL EntryIrql;
  134. MMWSLE ProtoProtect2;
  135. MMPTE TempPte2;
  136. #endif
  137. LOGICAL DirtyPte;
  138. ULONG FreeBit;
  139. MMPTE OriginalPte;
  140. MMWSLE ProtoProtect;
  141. PFILE_OBJECT FileObject;
  142. LONGLONG FileOffset;
  143. PSUBSECTION Subsection;
  144. MMSECTION_FLAGS ControlAreaFlags;
  145. ULONG Flags;
  146. LOGICAL PfnHeld;
  147. LOGICAL AccessCheckNeeded;
  148. PVOID UsedPageTableHandle;
  149. ULONG_PTR i;
  150. ULONG_PTR NumberOfProtos;
  151. ULONG_PTR MaxProtos;
  152. ULONG_PTR ProtosProcessed;
  153. MMPTE TempPte;
  154. MMPTE RealPteContents;
  155. NTSTATUS status;
  156. PMMINPAGE_SUPPORT ReadBlock;
  157. MMPTE SavedPte;
  158. PMMINPAGE_SUPPORT CapturedEvent;
  159. KIRQL OldIrql;
  160. PPFN_NUMBER Page;
  161. PFN_NUMBER PageFrameIndex;
  162. LONG NumberOfBytes;
  163. PMMPTE CheckPte;
  164. PMMPTE ReadPte;
  165. PMMPFN PfnClusterPage;
  166. PMMPFN Pfn1;
  167. PMMSUPPORT SessionWs;
  168. PETHREAD CurrentThread;
  169. PERFINFO_HARDPAGEFAULT_INFORMATION HardFaultEvent;
  170. LARGE_INTEGER IoStartTime;
  171. LARGE_INTEGER IoCompleteTime;
  172. LOGICAL PerfInfoLogHardFault;
  173. PETHREAD Thread;
  174. ULONG_PTR StoreInstruction;
  175. PMMPFN LockedProtoPfn;
  176. WSLE_NUMBER WorkingSetIndex;
  177. PMMPFN Pfn2;
  178. PMMPTE ContainingPageTablePointer;
  179. #if DBG
  180. EntryIrql = KeGetCurrentIrql ();
  181. ASSERT (EntryIrql <= APC_LEVEL);
  182. ASSERT (KeAreAllApcsDisabled () == TRUE);
  183. #endif
  184. LockedProtoPfn = NULL;
  185. SessionWs = NULL;
  186. StoreInstruction = MI_FAULT_STATUS_INDICATES_WRITE (FaultStatus);
  187. //
  188. // Initializing ReadBlock & ReadPte is not needed for correctness, but
  189. // without it the compiler cannot compile this code W4 to check for use of
  190. // uninitialized variables.
  191. //
  192. OldIrql = MM_NOIRQL;
  193. ReadPte = NULL;
  194. ReadBlock = NULL;
  195. ProtoProtect.u1.Long = 0;
  196. if (PointerProtoPte != NULL) {
  197. ASSERT (!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
  198. CheckPte = MiGetPteAddress (PointerProtoPte);
  199. if (VirtualAddress < MmSystemRangeStart) {
  200. NumberOfProtos = 1;
  201. DirtyPte = FALSE;
  202. SATISFY_OVERZEALOUS_COMPILER (UsedPageTableHandle = NULL);
  203. SATISFY_OVERZEALOUS_COMPILER (Thread = NULL);
  204. SATISFY_OVERZEALOUS_COMPILER (Pfn2 = NULL);
  205. if ((PointerPte->u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) &&
  206. (PointerPte->u.Proto.ReadOnly == 0)) {
  207. //
  208. // Kernel mode access must be verified, go the long way below.
  209. //
  210. AccessCheckNeeded = TRUE;
  211. }
  212. else {
  213. AccessCheckNeeded = FALSE;
  214. //
  215. // Opportunistically cluster the transition faults as needed.
  216. // When the Vad is non-NULL, the proper access checks have
  217. // already been applied across the range (as long as the
  218. // PTEs are zero).
  219. //
  220. if ((Vad != NULL) &&
  221. (MmAvailablePages > MM_ENORMOUS_LIMIT) &&
  222. (RecheckAccess == FALSE)) {
  223. NumberOfProtos = MmMaxTransitionCluster;
  224. //
  225. // Ensure the cluster doesn't cross the VAD contiguous PTE
  226. // limits.
  227. //
  228. MaxProtos = Vad->LastContiguousPte - PointerProtoPte + 1;
  229. if (NumberOfProtos > MaxProtos) {
  230. NumberOfProtos = MaxProtos;
  231. }
  232. //
  233. // Ensure the cluster doesn't cross the page containing
  234. // the real page table page as we only locked down the
  235. // single page.
  236. //
  237. MaxProtos = (PAGE_SIZE - BYTE_OFFSET (PointerPte)) / sizeof (MMPTE);
  238. if (NumberOfProtos > MaxProtos) {
  239. NumberOfProtos = MaxProtos;
  240. }
  241. //
  242. // Ensure the cluster doesn't cross the page containing
  243. // the prototype PTEs as we only locked down the single
  244. // page.
  245. //
  246. MaxProtos = (PAGE_SIZE - BYTE_OFFSET (PointerProtoPte)) / sizeof (MMPTE);
  247. if (NumberOfProtos > MaxProtos) {
  248. NumberOfProtos = MaxProtos;
  249. }
  250. //
  251. // Ensure the cluster doesn't cross the VAD limits.
  252. //
  253. MaxProtos = Vad->EndingVpn - MI_VA_TO_VPN (VirtualAddress) + 1;
  254. if (NumberOfProtos > MaxProtos) {
  255. NumberOfProtos = MaxProtos;
  256. }
  257. //
  258. // Ensure enough WSLEs are available so we cannot fail to
  259. // insert the cluster later.
  260. //
  261. MaxProtos = 1;
  262. WorkingSetIndex = MmWorkingSetList->FirstFree;
  263. if ((NumberOfProtos > 1) &&
  264. (WorkingSetIndex != WSLE_NULL_INDEX)) {
  265. do {
  266. if (MmWsle[WorkingSetIndex].u1.Long == (WSLE_NULL_INDEX << MM_FREE_WSLE_SHIFT)) {
  267. break;
  268. }
  269. MaxProtos += 1;
  270. WorkingSetIndex = (WSLE_NUMBER) (MmWsle[WorkingSetIndex].u1.Long >> MM_FREE_WSLE_SHIFT);
  271. } while (MaxProtos < NumberOfProtos);
  272. }
  273. if (NumberOfProtos > MaxProtos) {
  274. NumberOfProtos = MaxProtos;
  275. }
  276. //
  277. // We have computed the maximum cluster size. Fill the PTEs
  278. // and increment the use counts on the page table page for
  279. // each PTE we fill (regardless of whether the prototype
  280. // cluster pages are already in transition).
  281. //
  282. ASSERT (VirtualAddress <= MM_HIGHEST_USER_ADDRESS);
  283. for (i = 1; i < NumberOfProtos; i += 1) {
  284. if ((PointerPte + i)->u.Long != MM_ZERO_PTE) {
  285. break;
  286. }
  287. MI_WRITE_INVALID_PTE (PointerPte + i, *PointerPte);
  288. }
  289. NumberOfProtos = i;
  290. if (NumberOfProtos > 1) {
  291. UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (VirtualAddress);
  292. MI_INCREMENT_USED_PTES_BY_HANDLE_CLUSTER (UsedPageTableHandle, NumberOfProtos - 1);
  293. //
  294. // The protection code for the real PTE comes from
  295. // the real PTE as it was placed there earlier during
  296. // the handling of this fault.
  297. //
  298. ProtoProtect.u1.e1.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE (PointerPte);
  299. //
  300. // Build up a valid PTE so that when the PFN lock is
  301. // held, the only additional update to it is for the
  302. // actual PFN.
  303. //
  304. MI_MAKE_VALID_PTE (RealPteContents,
  305. 0,
  306. ProtoProtect.u1.e1.Protection,
  307. PointerPte);
  308. if ((StoreInstruction != 0) &&
  309. ((ProtoProtect.u1.e1.Protection & MM_COPY_ON_WRITE_MASK) != MM_COPY_ON_WRITE_MASK)) {
  310. MI_SET_PTE_DIRTY (RealPteContents);
  311. DirtyPte = TRUE;
  312. }
  313. ContainingPageTablePointer = MiGetPteAddress (PointerPte);
  314. Pfn2 = MI_PFN_ELEMENT (ContainingPageTablePointer->u.Hard.PageFrameNumber);
  315. Thread = PsGetCurrentThread ();
  316. }
  317. }
  318. }
  319. ProtosProcessed = 0;
  320. //
  321. // Acquire the PFN lock to synchronize access to prototype PTEs.
  322. // This is required as the working set mutex does not prevent
  323. // multiple processes from operating on the same prototype PTE.
  324. //
  325. PfnHeld = TRUE;
  326. LOCK_PFN (OldIrql);
  327. if (CheckPte->u.Hard.Valid == 0) {
  328. MiMakeSystemAddressValidPfn (PointerProtoPte, OldIrql);
  329. }
  330. TempPte = *PointerProtoPte;
  331. if (RecheckAccess == TRUE) {
  332. //
  333. // This is a forked process so shared prototype PTEs
  334. // may actually be fork clone prototypes. These have
  335. // the protection within the fork clone yet the
  336. // hardware PTEs always share it. This must be
  337. // checked here for the case where the NO_ACCESS
  338. // permission has been put into the fork clone because
  339. // it would not necessarily be in the hardware PTEs like
  340. // it is for normal prototypes.
  341. //
  342. // First make sure the proto is in transition or paged
  343. // out as only these states can be no access.
  344. //
  345. if ((TempPte.u.Hard.Valid == 0) &&
  346. (TempPte.u.Soft.Prototype == 0)) {
  347. ProtoProtect.u1.e1.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE (&TempPte);
  348. if (ProtoProtect.u1.e1.Protection == MM_NOACCESS) {
  349. ASSERT (MiLocateCloneAddress (Process, PointerProtoPte) != NULL);
  350. UNLOCK_PFN (OldIrql);
  351. return STATUS_ACCESS_VIOLATION;
  352. }
  353. }
  354. }
  355. //
  356. // If the fault can be handled inline (prototype transition or
  357. // valid for example), then process it here (eliminating
  358. // locked page charges, etc) to reduce PFN hold times.
  359. //
  360. if (AccessCheckNeeded == FALSE) {
  361. while (TRUE) {
  362. if (TempPte.u.Hard.Valid == 1) {
  363. //
  364. // Prototype PTE is valid.
  365. //
  366. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
  367. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  368. Pfn1->u2.ShareCount += 1;
  369. PERFINFO_SOFTFAULT (Pfn1,
  370. VirtualAddress,
  371. PERFINFO_LOG_TYPE_ADDVALIDPAGETOWS)
  372. }
  373. else if ((TempPte.u.Soft.Prototype == 0) &&
  374. (TempPte.u.Soft.Transition == 1)) {
  375. //
  376. // This is a fault on a PTE which ultimately
  377. // decodes to a prototype PTE referencing a
  378. // page already in the cache.
  379. //
  380. // Optimize this path as every cycle counts.
  381. //
  382. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&TempPte);
  383. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  384. ASSERT (Pfn1->u3.e1.PageLocation != ActiveAndValid);
  385. if ((Pfn1->u3.e1.ReadInProgress == 1) ||
  386. (Pfn1->u4.InPageError == 1) ||
  387. (MmAvailablePages < MM_HIGH_LIMIT)) {
  388. break;
  389. }
  390. MiUnlinkPageFromList (Pfn1);
  391. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  392. //
  393. // Update the PFN database - the reference count
  394. // must be incremented as the share count is
  395. // going to go from zero to 1.
  396. //
  397. ASSERT (Pfn1->u2.ShareCount == 0);
  398. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  399. //
  400. // The PFN reference count will be 1 already
  401. // here if the modified writer has begun a write
  402. // of this page. Otherwise it's ordinarily 0.
  403. //
  404. // Note there is no need to apply locked page
  405. // charges for this page as we know that the share
  406. // count is zero (and the reference count is
  407. // unknown). But since we are incrementing both
  408. // the share and reference counts by one, the
  409. // page will retain its current locked charge
  410. // regardless of whether or not it is currently
  411. // set.
  412. //
  413. Pfn1->u3.e2.ReferenceCount += 1;
  414. //
  415. // Update the transition PTE.
  416. //
  417. Pfn1->u2.ShareCount += 1;
  418. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  419. PERFINFO_SOFTFAULT (Pfn1,
  420. VirtualAddress,
  421. PERFINFO_LOG_TYPE_ADDVALIDPAGETOWS)
  422. MI_MAKE_TRANSITION_PROTOPTE_VALID (TempPte,
  423. PointerProtoPte);
  424. //
  425. // If the modified field is set in the PFN database
  426. // and this page is not copy on write, then set
  427. // the dirty bit. This can be done as the modified
  428. // page will not be written to the paging file
  429. // until this PTE is made invalid.
  430. //
  431. if ((Pfn1->u3.e1.Modified) &&
  432. (TempPte.u.Hard.Write) &&
  433. (TempPte.u.Hard.CopyOnWrite == 0)) {
  434. MI_SET_PTE_DIRTY (TempPte);
  435. }
  436. else {
  437. MI_SET_PTE_CLEAN (TempPte);
  438. }
  439. MI_WRITE_VALID_PTE (PointerProtoPte, TempPte);
  440. ASSERT (PointerPte->u.Hard.Valid == 0);
  441. }
  442. else {
  443. break;
  444. }
  445. ProtosProcessed += 1;
  446. if (ProtosProcessed == NumberOfProtos) {
  447. //
  448. // This is the last (or only) PFN so use
  449. // MiCompleteProtoPteFault so the PFN lock is released
  450. // as quickly as possible.
  451. //
  452. MiCompleteProtoPteFault (StoreInstruction,
  453. VirtualAddress,
  454. PointerPte,
  455. PointerProtoPte,
  456. OldIrql,
  457. &LockedProtoPfn);
  458. PfnHeld = FALSE;
  459. break;
  460. }
  461. //
  462. // Just finish the PFN work here but not the working
  463. // set or prefetcher actions as the PFN lock is held
  464. // and we want to minimize PFN hold time.
  465. //
  466. ASSERT (PointerProtoPte->u.Hard.Valid == 1);
  467. Pfn1->u3.e1.PrototypePte = 1;
  468. //
  469. // Prototype PTE is now valid, make the PTE valid.
  470. //
  471. // A PTE just went from not present, not transition to
  472. // present. The share count and valid count must be
  473. // updated in the page table page which contains this PTE.
  474. //
  475. Pfn2->u2.ShareCount += 1;
  476. RealPteContents.u.Hard.PageFrameNumber = PageFrameIndex;
  477. #if DBG
  478. //
  479. // The protection code for the real PTE comes from
  480. // the real PTE as it was placed there above.
  481. //
  482. ProtoProtect2.u1.Long = 0;
  483. ASSERT (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
  484. ProtoProtect2.u1.e1.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(PointerPte);
  485. MI_MAKE_VALID_PTE (TempPte2,
  486. PageFrameIndex,
  487. ProtoProtect2.u1.e1.Protection,
  488. PointerPte);
  489. if ((StoreInstruction != 0) &&
  490. ((ProtoProtect2.u1.e1.Protection & MM_COPY_ON_WRITE_MASK) != MM_COPY_ON_WRITE_MASK)) {
  491. MI_SET_PTE_DIRTY (TempPte2);
  492. }
  493. ASSERT (TempPte2.u.Long == RealPteContents.u.Long);
  494. #endif
  495. MI_SNAP_DATA (Pfn1, PointerProtoPte, 6);
  496. //
  497. // If this is a store instruction and the page is not
  498. // copy on write, then set the modified bit in the PFN
  499. // database and the dirty bit in the PTE. The PTE is
  500. // not set dirty even if the modified bit is set so
  501. // writes to the page can be tracked for FlushVirtualMemory.
  502. //
  503. if (DirtyPte == TRUE) {
  504. OriginalPte = Pfn1->OriginalPte;
  505. #if DBG
  506. if (OriginalPte.u.Soft.Prototype == 1) {
  507. PCONTROL_AREA ControlArea;
  508. Subsection = MiGetSubsectionAddress (&OriginalPte);
  509. ControlArea = Subsection->ControlArea;
  510. if (ControlArea->DereferenceList.Flink != NULL) {
  511. DbgPrint ("MM: page fault completing to dereferenced CA %p %p %p\n",
  512. ControlArea, Pfn1, PointerPte);
  513. DbgBreakPoint ();
  514. }
  515. }
  516. #endif
  517. MI_SET_MODIFIED (Pfn1, 1, 0xA);
  518. if ((OriginalPte.u.Soft.Prototype == 0) &&
  519. (Pfn1->u3.e1.WriteInProgress == 0)) {
  520. FreeBit = GET_PAGING_FILE_OFFSET (OriginalPte);
  521. if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
  522. MiReleaseConfirmedPageFileSpace (OriginalPte);
  523. }
  524. Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
  525. }
  526. }
  527. ASSERT (PointerPte == MiGetPteAddress (VirtualAddress));
  528. MI_WRITE_VALID_PTE (PointerPte, RealPteContents);
  529. PERFINFO_SOFTFAULT(Pfn1, VirtualAddress, PERFINFO_LOG_TYPE_PROTOPTEFAULT);
  530. PointerProtoPte += 1;
  531. TempPte = *PointerProtoPte;
  532. PointerPte += 1;
  533. VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
  534. }
  535. }
  536. if (ProtosProcessed != 0) {
  537. //
  538. // At least the first VA was handled and that was the one
  539. // that caused the fault so just return now as any other
  540. // VAs were purely optional.
  541. //
  542. if (PfnHeld == TRUE) {
  543. //
  544. // The last speculative VA was not made valid and the PFN
  545. // lock is still held. Release the PFN lock now.
  546. //
  547. UNLOCK_PFN (OldIrql);
  548. InterlockedExchangeAdd ((PLONG) &MmInfoCounters.TransitionCount,
  549. (LONG) ProtosProcessed);
  550. }
  551. else {
  552. //
  553. // The last speculative VA was made valid and was also
  554. // inserted into the working set list. Subtract one from
  555. // the count of protos that need working set insertions
  556. // below.
  557. //
  558. InterlockedExchangeAdd ((PLONG) &MmInfoCounters.TransitionCount,
  559. (LONG) ProtosProcessed);
  560. ProtosProcessed -= 1;
  561. }
  562. //
  563. // Back the locals up to the last "made-valid" VA where
  564. // the working set insertions need to begin.
  565. //
  566. // Add working set entries for the cluster of addresses.
  567. //
  568. // Note because we checked the WSLE list above prior
  569. // to clustering (and the working set mutex has never been
  570. // released), we are guaranteed that the working set list
  571. // insertions below cannot fail.
  572. //
  573. Subsection = NULL;
  574. SATISFY_OVERZEALOUS_COMPILER (FileObject = NULL);
  575. SATISFY_OVERZEALOUS_COMPILER (FileOffset = 0);
  576. SATISFY_OVERZEALOUS_COMPILER (Flags = 0);
  577. while (ProtosProcessed != 0) {
  578. PointerProtoPte -= 1;
  579. PointerPte -= 1;
  580. VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress - PAGE_SIZE);
  581. ProtosProcessed -= 1;
  582. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  583. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  584. ASSERT (ProtoProtect.u1.e1.Protection != 0);
  585. ASSERT (MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
  586. ASSERT (PointerPte->u.Hard.Valid == 1);
  587. PERFINFO_ADDTOWS (Pfn1, VirtualAddress, Process->UniqueProcessId)
  588. WorkingSetIndex = MiAllocateWsle (&Process->Vm,
  589. PointerPte,
  590. Pfn1,
  591. ProtoProtect.u1.Long);
  592. ASSERT (WorkingSetIndex != 0);
  593. //
  594. // Log prefetch fault information.
  595. //
  596. // Note that the process' working set mutex is still
  597. // held so any other faults or operations on user
  598. // addresses by other threads in this process
  599. // will block for the duration of this call.
  600. //
  601. if ((Subsection == NULL) &&
  602. (CCPF_IS_PREFETCHER_ACTIVE()) &&
  603. (Pfn1->OriginalPte.u.Soft.Prototype == 1)) {
  604. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  605. FileObject = Subsection->ControlArea->FilePointer;
  606. FileOffset = MiStartingOffset (Subsection, PointerProtoPte);
  607. Flags = 0;
  608. ControlAreaFlags = Subsection->ControlArea->u.Flags;
  609. //
  610. // Image pages are not speculatively transition
  611. // clustered so this must be a data page we are telling
  612. // the prefetcher about.
  613. //
  614. ASSERT (ControlAreaFlags.Image == 0);
  615. if (ControlAreaFlags.Rom) {
  616. Flags |= CCPF_TYPE_ROM;
  617. }
  618. }
  619. if (Subsection != NULL) {
  620. CcPfLogPageFault (FileObject, FileOffset, Flags);
  621. }
  622. }
  623. #if DBG
  624. if (EntryIrql != KeGetCurrentIrql ()) {
  625. DbgPrint ("PAGFAULT: IRQL0 mismatch %x %x\n",
  626. EntryIrql, KeGetCurrentIrql ());
  627. }
  628. if (KeGetCurrentIrql() > APC_LEVEL) {
  629. DbgPrint ("PAGFAULT: IRQL1 mismatch %x %x\n",
  630. EntryIrql, KeGetCurrentIrql ());
  631. }
  632. #endif
  633. ASSERT (KeAreAllApcsDisabled () == TRUE);
  634. return STATUS_PAGE_FAULT_TRANSITION;
  635. }
  636. ASSERT (PfnHeld == TRUE);
  637. LockedProtoPfn = MI_PFN_ELEMENT (CheckPte->u.Hard.PageFrameNumber);
  638. MI_ADD_LOCKED_PAGE_CHARGE(LockedProtoPfn, TRUE, 2);
  639. LockedProtoPfn->u3.e2.ReferenceCount += 1;
  640. ASSERT (LockedProtoPfn->u3.e2.ReferenceCount > 1);
  641. ASSERT (PointerPte->u.Hard.Valid == 0);
  642. }
  643. else {
  644. LOCK_PFN (OldIrql);
  645. if (CheckPte->u.Hard.Valid == 0) {
  646. //
  647. // Make sure the prototype PTEs are in memory. If not, since
  648. // this is a system address, just convert the fault as though
  649. // it happened on the prototype PTE instead.
  650. //
  651. ASSERT ((Process == NULL) || (Process == HYDRA_PROCESS));
  652. UNLOCK_PFN (OldIrql);
  653. VirtualAddress = PointerProtoPte;
  654. PointerPte = CheckPte;
  655. PointerProtoPte = NULL;
  656. //
  657. // The page that contains the prototype PTE is not in memory.
  658. //
  659. if (Process == HYDRA_PROCESS) {
  660. //
  661. // We were called while holding this session space's
  662. // working set lock. But we need to fault in a
  663. // prototype PTE which is in system paged pool. This
  664. // must be done under the system working set lock.
  665. //
  666. // So we release the session space WSL lock and get
  667. // the system working set lock. When done
  668. // we return STATUS_MORE_PROCESSING_REQUIRED
  669. // so our caller will call us again to handle the
  670. // actual prototype PTE fault.
  671. //
  672. ASSERT (MI_IS_SESSION_ADDRESS (VirtualAddress) == FALSE);
  673. SessionWs = &MmSessionSpace->GlobalVirtualAddress->Vm;
  674. UNLOCK_WORKING_SET (SessionWs);
  675. //
  676. // Clear Process as the system working set is now held.
  677. //
  678. Process = NULL;
  679. LOCK_SYSTEM_WS (PsGetCurrentThread ());
  680. }
  681. goto NonProtoFault;
  682. }
  683. else if (PointerPte->u.Hard.Valid == 1) {
  684. //
  685. // PTE was already made valid by the cache manager support
  686. // routines.
  687. //
  688. UNLOCK_PFN (OldIrql);
  689. return STATUS_SUCCESS;
  690. }
  691. }
  692. status = MiResolveProtoPteFault (StoreInstruction,
  693. VirtualAddress,
  694. PointerPte,
  695. PointerProtoPte,
  696. &LockedProtoPfn,
  697. &ReadBlock,
  698. Process,
  699. OldIrql,
  700. ApcNeeded);
  701. //
  702. // Returns with PFN lock released.
  703. //
  704. ReadPte = PointerProtoPte;
  705. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  706. ASSERT (KeAreAllApcsDisabled () == TRUE);
  707. }
  708. else {
  709. NonProtoFault:
  710. TempPte = *PointerPte;
  711. ASSERT (TempPte.u.Long != 0);
  712. if (TempPte.u.Soft.Transition != 0) {
  713. //
  714. // This is a transition page.
  715. //
  716. CapturedEvent = NULL;
  717. status = MiResolveTransitionFault (VirtualAddress,
  718. PointerPte,
  719. Process,
  720. MM_NOIRQL,
  721. ApcNeeded,
  722. &CapturedEvent);
  723. if (CapturedEvent != NULL) {
  724. MiFreeInPageSupportBlock (CapturedEvent);
  725. }
  726. }
  727. else if (TempPte.u.Soft.PageFileHigh == 0) {
  728. //
  729. // Demand zero fault.
  730. //
  731. status = MiResolveDemandZeroFault (VirtualAddress,
  732. PointerPte,
  733. Process,
  734. MM_NOIRQL);
  735. }
  736. else {
  737. //
  738. // Page resides in paging file.
  739. //
  740. ReadPte = PointerPte;
  741. LOCK_PFN (OldIrql);
  742. TempPte = *PointerPte;
  743. ASSERT (TempPte.u.Long != 0);
  744. if ((TempPte.u.Hard.Valid == 0) &&
  745. (TempPte.u.Soft.Prototype == 0) &&
  746. (TempPte.u.Soft.Transition == 0)) {
  747. status = MiResolvePageFileFault (VirtualAddress,
  748. PointerPte,
  749. &ReadBlock,
  750. Process,
  751. OldIrql);
  752. }
  753. else {
  754. UNLOCK_PFN (OldIrql);
  755. status = STATUS_REFAULT;
  756. }
  757. }
  758. }
  759. //
  760. // Issue the I/O and/or finish completing the soft fault.
  761. //
  762. ASSERT (KeAreAllApcsDisabled () == TRUE);
  763. if (NT_SUCCESS(status)) {
  764. if (LockedProtoPfn != NULL) {
  765. //
  766. // Unlock page containing prototype PTEs.
  767. //
  768. ASSERT (PointerProtoPte != NULL);
  769. LOCK_PFN (OldIrql);
  770. //
  771. // The reference count on the prototype PTE page will
  772. // always be greater than 1 if it is a genuine prototype
  773. // PTE pool allocation. However, if it is a fork
  774. // prototype PTE allocation, it is possible the pool has
  775. // already been deallocated and in this case, the LockedProtoPfn
  776. // frame below will be in transition limbo with a share
  777. // count of 0 and a reference count of 1 awaiting our
  778. // final dereference below which will put it on the free list.
  779. //
  780. ASSERT (LockedProtoPfn->u3.e2.ReferenceCount >= 1);
  781. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (LockedProtoPfn, 3);
  782. UNLOCK_PFN (OldIrql);
  783. }
  784. if (SessionWs != NULL) {
  785. UNLOCK_SYSTEM_WS ();
  786. LOCK_WORKING_SET (SessionWs);
  787. }
  788. #if DBG
  789. if (EntryIrql != KeGetCurrentIrql ()) {
  790. DbgPrint ("PAGFAULT: IRQL0 mismatch %x %x\n",
  791. EntryIrql, KeGetCurrentIrql ());
  792. }
  793. if (KeGetCurrentIrql() > APC_LEVEL) {
  794. DbgPrint ("PAGFAULT: IRQL1 mismatch %x %x\n",
  795. EntryIrql, KeGetCurrentIrql ());
  796. }
  797. #endif
  798. return status;
  799. }
  800. if (status == STATUS_ISSUE_PAGING_IO) {
  801. ASSERT (ReadPte != NULL);
  802. ASSERT (ReadBlock != NULL);
  803. SavedPte = *ReadPte;
  804. CapturedEvent = (PMMINPAGE_SUPPORT)ReadBlock->Pfn->u1.Event;
  805. if (Process == HYDRA_PROCESS) {
  806. UNLOCK_WORKING_SET (&MmSessionSpace->GlobalVirtualAddress->Vm);
  807. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  808. ASSERT (KeAreAllApcsDisabled () == TRUE);
  809. CurrentThread = NULL;
  810. }
  811. else if (Process != NULL) {
  812. //
  813. // APCs must be explicitly disabled to prevent suspend APCs from
  814. // interrupting this thread before the I/O has been issued.
  815. // Otherwise a shared page I/O can stop any other thread that
  816. // references it indefinitely until the suspend is released.
  817. //
  818. CurrentThread = PsGetCurrentThread ();
  819. ASSERT (CurrentThread->NestedFaultCount <= 2);
  820. CurrentThread->NestedFaultCount += 1;
  821. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  822. UNLOCK_WS (Process);
  823. }
  824. else {
  825. UNLOCK_SYSTEM_WS ();
  826. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  827. ASSERT (KeAreAllApcsDisabled () == TRUE);
  828. CurrentThread = NULL;
  829. }
  830. #if DBG
  831. if (MmDebug & MM_DBG_PAGEFAULT) {
  832. DbgPrint ("MMFAULT: va: %p size: %lx process: %s file: %Z\n",
  833. VirtualAddress,
  834. ReadBlock->Mdl.ByteCount,
  835. Process == HYDRA_PROCESS ? (PUCHAR)"Session Space" : (Process ? Process->ImageFileName : (PUCHAR)"SystemVa"),
  836. &ReadBlock->FilePointer->FileName
  837. );
  838. }
  839. #endif //DBG
  840. if (PERFINFO_IS_GROUP_ON(PERF_FILE_IO)) {
  841. PerfInfoLogHardFault = TRUE;
  842. PerfTimeStamp (IoStartTime);
  843. }
  844. else {
  845. PerfInfoLogHardFault = FALSE;
  846. SATISFY_OVERZEALOUS_COMPILER (IoStartTime.QuadPart = 0);
  847. }
  848. IoCompleteTime.QuadPart = 0;
  849. //
  850. // Assert no reads issued here are marked as prefetched.
  851. //
  852. ASSERT (ReadBlock->u1.e1.PrefetchMdlHighBits == 0);
  853. //
  854. // Issue the read request.
  855. //
  856. status = IoPageRead (ReadBlock->FilePointer,
  857. &ReadBlock->Mdl,
  858. &ReadBlock->ReadOffset,
  859. &ReadBlock->Event,
  860. &ReadBlock->IoStatus);
  861. if (!NT_SUCCESS(status)) {
  862. //
  863. // Set the event as the I/O system doesn't set it on errors.
  864. //
  865. ReadBlock->IoStatus.Status = status;
  866. ReadBlock->IoStatus.Information = 0;
  867. KeSetEvent (&ReadBlock->Event, 0, FALSE);
  868. }
  869. //
  870. // Initializing PageFrameIndex is not needed for correctness, but
  871. // without it the compiler cannot compile this code W4 to check
  872. // for use of uninitialized variables.
  873. //
  874. PageFrameIndex = (PFN_NUMBER)-1;
  875. //
  876. // Wait for the I/O operation.
  877. //
  878. status = MiWaitForInPageComplete (ReadBlock->Pfn,
  879. ReadPte,
  880. VirtualAddress,
  881. &SavedPte,
  882. CapturedEvent,
  883. Process);
  884. if (CurrentThread != NULL) {
  885. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  886. ASSERT (CurrentThread->NestedFaultCount <= 3);
  887. ASSERT (CurrentThread->NestedFaultCount != 0);
  888. CurrentThread->NestedFaultCount -= 1;
  889. if ((CurrentThread->ApcNeeded == 1) &&
  890. (CurrentThread->NestedFaultCount == 0)) {
  891. *ApcNeeded = TRUE;
  892. CurrentThread->ApcNeeded = 0;
  893. }
  894. }
  895. if (PerfInfoLogHardFault) {
  896. PerfTimeStamp (IoCompleteTime);
  897. }
  898. //
  899. // MiWaitForInPageComplete RETURNS WITH THE WORKING SET LOCK
  900. // AND PFN LOCK HELD!!!
  901. //
  902. //
  903. // This is the thread which owns the event, clear the event field
  904. // in the PFN database.
  905. //
  906. Pfn1 = ReadBlock->Pfn;
  907. Page = &ReadBlock->Page[0];
  908. NumberOfBytes = (LONG)ReadBlock->Mdl.ByteCount;
  909. CheckPte = ReadBlock->BasePte;
  910. while (NumberOfBytes > 0) {
  911. //
  912. // Don't remove the page we just brought in to
  913. // satisfy this page fault.
  914. //
  915. if (CheckPte != ReadPte) {
  916. PfnClusterPage = MI_PFN_ELEMENT (*Page);
  917. MI_SNAP_DATA (PfnClusterPage, PfnClusterPage->PteAddress, 0xB);
  918. ASSERT (PfnClusterPage->u4.PteFrame == Pfn1->u4.PteFrame);
  919. #if DBG
  920. if (PfnClusterPage->u4.InPageError) {
  921. ASSERT (status != STATUS_SUCCESS);
  922. }
  923. #endif
  924. if (PfnClusterPage->u3.e1.ReadInProgress != 0) {
  925. ASSERT (PfnClusterPage->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  926. PfnClusterPage->u3.e1.ReadInProgress = 0;
  927. if (PfnClusterPage->u4.InPageError == 0) {
  928. PfnClusterPage->u1.Event = NULL;
  929. }
  930. }
  931. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(PfnClusterPage, 9);
  932. }
  933. else {
  934. PageFrameIndex = *Page;
  935. MI_SNAP_DATA (MI_PFN_ELEMENT (PageFrameIndex),
  936. MI_PFN_ELEMENT (PageFrameIndex)->PteAddress,
  937. 0xC);
  938. }
  939. CheckPte += 1;
  940. Page += 1;
  941. NumberOfBytes -= PAGE_SIZE;
  942. }
  943. if (status != STATUS_SUCCESS) {
  944. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(MI_PFN_ELEMENT(PageFrameIndex), 9);
  945. if (status != STATUS_PTE_CHANGED) {
  946. //
  947. // An I/O error occurred during the page read
  948. // operation. All the pages which were just
  949. // put into transition should be put onto the
  950. // free list if InPageError is set, and their
  951. // PTEs restored to the proper contents.
  952. //
  953. Page = &ReadBlock->Page[0];
  954. NumberOfBytes = ReadBlock->Mdl.ByteCount;
  955. while (NumberOfBytes > 0) {
  956. PfnClusterPage = MI_PFN_ELEMENT (*Page);
  957. if ((PfnClusterPage->u4.InPageError == 1) &&
  958. (PfnClusterPage->u3.e2.ReferenceCount == 0)) {
  959. PfnClusterPage->u4.InPageError = 0;
  960. //
  961. // Only restore the transition PTE if the address
  962. // space still exists. Another thread may have
  963. // deleted the VAD while this thread waited for the
  964. // fault to complete - in this case, the frame
  965. // will be marked as free already.
  966. //
  967. if (PfnClusterPage->u3.e1.PageLocation != FreePageList) {
  968. ASSERT (PfnClusterPage->u3.e1.PageLocation ==
  969. StandbyPageList);
  970. MiUnlinkPageFromList (PfnClusterPage);
  971. ASSERT (PfnClusterPage->u3.e2.ReferenceCount == 0);
  972. MiRestoreTransitionPte (PfnClusterPage);
  973. MiInsertPageInFreeList (*Page);
  974. }
  975. }
  976. Page += 1;
  977. NumberOfBytes -= PAGE_SIZE;
  978. }
  979. }
  980. if (LockedProtoPfn != NULL) {
  981. //
  982. // Unlock page containing prototype PTEs.
  983. //
  984. ASSERT (PointerProtoPte != NULL);
  985. //
  986. // The reference count on the prototype PTE page will
  987. // always be greater than 1 if it is a genuine prototype
  988. // PTE pool allocation. However, if it is a fork
  989. // prototype PTE allocation, it is possible the pool has
  990. // already been deallocated and in this case, the LockedProtoPfn
  991. // frame below will be in transition limbo with a share
  992. // count of 0 and a reference count of 1 awaiting our
  993. // final dereference below which will put it on the free list.
  994. //
  995. ASSERT (LockedProtoPfn->u3.e2.ReferenceCount >= 1);
  996. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (LockedProtoPfn, 3);
  997. }
  998. UNLOCK_PFN (OldIrql);
  999. if (SessionWs != NULL) {
  1000. UNLOCK_SYSTEM_WS ();
  1001. ASSERT (KeAreAllApcsDisabled () == TRUE);
  1002. LOCK_WORKING_SET (SessionWs);
  1003. }
  1004. MiFreeInPageSupportBlock (CapturedEvent);
  1005. if (status == STATUS_PTE_CHANGED) {
  1006. //
  1007. // State of PTE changed during I/O operation, just
  1008. // return success and refault.
  1009. //
  1010. status = STATUS_SUCCESS;
  1011. }
  1012. else if (status == STATUS_REFAULT) {
  1013. //
  1014. // The I/O operation to bring in a system page failed
  1015. // due to insufficent resources. Set the status to one
  1016. // of the MmIsRetryIoStatus codes so our caller will
  1017. // delay and retry.
  1018. //
  1019. status = STATUS_NO_MEMORY;
  1020. }
  1021. ASSERT (EntryIrql == KeGetCurrentIrql ());
  1022. return status;
  1023. }
  1024. //
  1025. // PTE is still in transition state, same protection, etc.
  1026. //
  1027. ASSERT (Pfn1->u4.InPageError == 0);
  1028. if (Pfn1->u2.ShareCount == 0) {
  1029. MI_REMOVE_LOCKED_PAGE_CHARGE (Pfn1, 9);
  1030. }
  1031. Pfn1->u2.ShareCount += 1;
  1032. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1033. Pfn1->u3.e1.CacheAttribute = MiCached;
  1034. MI_MAKE_TRANSITION_PTE_VALID (TempPte, ReadPte);
  1035. if (StoreInstruction && TempPte.u.Hard.Write) {
  1036. MI_SET_PTE_DIRTY (TempPte);
  1037. }
  1038. MI_WRITE_VALID_PTE (ReadPte, TempPte);
  1039. if (PointerProtoPte != NULL) {
  1040. //
  1041. // The prototype PTE has been made valid, now make the
  1042. // original PTE valid. The original PTE must still be invalid
  1043. // otherwise MiWaitForInPageComplete would have returned
  1044. // a collision status.
  1045. //
  1046. ASSERT (PointerPte->u.Hard.Valid == 0);
  1047. //
  1048. // PTE is not valid, continue with operation.
  1049. //
  1050. status = MiCompleteProtoPteFault (StoreInstruction,
  1051. VirtualAddress,
  1052. PointerPte,
  1053. PointerProtoPte,
  1054. OldIrql,
  1055. &LockedProtoPfn);
  1056. //
  1057. // Returns with PFN lock released!
  1058. //
  1059. ASSERT (KeAreAllApcsDisabled () == TRUE);
  1060. }
  1061. else {
  1062. ASSERT (LockedProtoPfn == NULL);
  1063. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  1064. UNLOCK_PFN (OldIrql);
  1065. WorkingSetIndex = MiAddValidPageToWorkingSet (VirtualAddress,
  1066. ReadPte,
  1067. Pfn1,
  1068. 0);
  1069. if (WorkingSetIndex == 0) {
  1070. //
  1071. // Trim the page since we couldn't add it to the working
  1072. // set list at this time.
  1073. //
  1074. MiTrimPte (VirtualAddress,
  1075. ReadPte,
  1076. Pfn1,
  1077. Process,
  1078. ZeroPte);
  1079. status = STATUS_NO_MEMORY;
  1080. }
  1081. ASSERT (KeAreAllApcsDisabled () == TRUE);
  1082. }
  1083. if (PerfInfoLogHardFault) {
  1084. Thread = PsGetCurrentThread ();
  1085. HardFaultEvent.ReadOffset = ReadBlock->ReadOffset;
  1086. HardFaultEvent.IoTime.QuadPart = IoCompleteTime.QuadPart - IoStartTime.QuadPart;
  1087. HardFaultEvent.VirtualAddress = VirtualAddress;
  1088. HardFaultEvent.FileObject = ReadBlock->FilePointer;
  1089. HardFaultEvent.ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
  1090. HardFaultEvent.ByteCount = ReadBlock->Mdl.ByteCount;
  1091. PerfInfoLogBytes(PERFINFO_LOG_TYPE_HARDFAULT, &HardFaultEvent, sizeof(HardFaultEvent));
  1092. }
  1093. MiFreeInPageSupportBlock (CapturedEvent);
  1094. if (status == STATUS_SUCCESS) {
  1095. status = STATUS_PAGE_FAULT_PAGING_FILE;
  1096. }
  1097. }
  1098. if ((status == STATUS_REFAULT) || (status == STATUS_PTE_CHANGED)) {
  1099. status = STATUS_SUCCESS;
  1100. }
  1101. ASSERT (KeAreAllApcsDisabled () == TRUE);
  1102. if (SessionWs != NULL) {
  1103. UNLOCK_SYSTEM_WS ();
  1104. ASSERT (KeAreAllApcsDisabled () == TRUE);
  1105. LOCK_WORKING_SET (SessionWs);
  1106. }
  1107. if (LockedProtoPfn != NULL) {
  1108. //
  1109. // Unlock page containing prototype PTEs.
  1110. //
  1111. ASSERT (PointerProtoPte != NULL);
  1112. LOCK_PFN (OldIrql);
  1113. //
  1114. // The reference count on the prototype PTE page will
  1115. // always be greater than 1 if it is a genuine prototype
  1116. // PTE pool allocation. However, if it is a fork
  1117. // prototype PTE allocation, it is possible the pool has
  1118. // already been deallocated and in this case, the LockedProtoPfn
  1119. // frame below will be in transition limbo with a share
  1120. // count of 0 and a reference count of 1 awaiting our
  1121. // final dereference below which will put it on the free list.
  1122. //
  1123. ASSERT (LockedProtoPfn->u3.e2.ReferenceCount >= 1);
  1124. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (LockedProtoPfn, 3);
  1125. UNLOCK_PFN (OldIrql);
  1126. }
  1127. ASSERT (EntryIrql == KeGetCurrentIrql ());
  1128. ASSERT (KeAreAllApcsDisabled () == TRUE);
  1129. return status;
  1130. }
  1131. NTSTATUS
  1132. MiResolveDemandZeroFault (
  1133. IN PVOID VirtualAddress,
  1134. IN PMMPTE PointerPte,
  1135. IN PEPROCESS Process,
  1136. IN KIRQL OldIrql
  1137. )
  1138. /*++
  1139. Routine Description:
  1140. This routine resolves a demand zero page fault.
  1141. Arguments:
  1142. VirtualAddress - Supplies the faulting address.
  1143. PointerPte - Supplies the PTE for the faulting address.
  1144. Process - Supplies a pointer to the process object. If this
  1145. parameter is NULL, then the fault is for system
  1146. space and the process's working set lock is not held.
  1147. OldIrql - Supplies the IRQL the caller acquired the PFN lock at (MM_NOIRQL
  1148. if the caller does not hold the PFN lock). If the caller holds
  1149. the PFN lock, the lock cannot be dropped, and the page should
  1150. not be added to the working set at this time.
  1151. Return Value:
  1152. NTSTATUS.
  1153. Environment:
  1154. Kernel mode, PFN lock held conditionally.
  1155. --*/
  1156. {
  1157. PMMPFN Pfn1;
  1158. PFN_NUMBER PageFrameIndex;
  1159. MMPTE TempPte;
  1160. ULONG PageColor;
  1161. LOGICAL NeedToZero;
  1162. LOGICAL BarrierNeeded;
  1163. ULONG BarrierStamp;
  1164. WSLE_NUMBER WorkingSetIndex;
  1165. LOGICAL ZeroPageNeeded;
  1166. LOGICAL CallerHeldPfn;
  1167. NeedToZero = FALSE;
  1168. BarrierNeeded = FALSE;
  1169. CallerHeldPfn = TRUE;
  1170. //
  1171. // Initializing BarrierStamp is not needed for
  1172. // correctness but without it the compiler cannot compile this code
  1173. // W4 to check for use of uninitialized variables.
  1174. //
  1175. BarrierStamp = 0;
  1176. PERFINFO_PRIVATE_PAGE_DEMAND_ZERO (VirtualAddress);
  1177. //
  1178. // Initialize variables assuming the operation will succeed.
  1179. // If it fails (lack of pages or whatever), it's ok that the
  1180. // process' NextPageColor got bumped anyway. The goal is to do
  1181. // as much as possible without holding the PFN lock.
  1182. //
  1183. if ((Process > HYDRA_PROCESS) && (OldIrql == MM_NOIRQL)) {
  1184. ASSERT (MI_IS_PAGE_TABLE_ADDRESS (PointerPte));
  1185. //
  1186. // If a fork operation is in progress and the faulting thread
  1187. // is not the thread performing the fork operation, block until
  1188. // the fork is completed.
  1189. //
  1190. if (Process->ForkInProgress != NULL) {
  1191. if (MiWaitForForkToComplete (Process) == TRUE) {
  1192. return STATUS_REFAULT;
  1193. }
  1194. }
  1195. PageColor = MI_PAGE_COLOR_VA_PROCESS (VirtualAddress,
  1196. &Process->NextPageColor);
  1197. ASSERT (PageColor != 0xFFFFFFFF);
  1198. ZeroPageNeeded = TRUE;
  1199. }
  1200. else {
  1201. if (OldIrql != MM_NOIRQL) {
  1202. ZeroPageNeeded = TRUE;
  1203. }
  1204. else {
  1205. ZeroPageNeeded = FALSE;
  1206. //
  1207. // For session space, the BSS of an image is typically mapped
  1208. // directly as an image, but in the case of images that have
  1209. // outstanding user references at the time of section creation,
  1210. // the image is copied to a pagefile backed section and then
  1211. // mapped in session view space (the destination is mapped in
  1212. // system view space). See MiSessionWideReserveImageAddress.
  1213. //
  1214. if ((Process == HYDRA_PROCESS) &&
  1215. ((MI_IS_SESSION_IMAGE_ADDRESS (VirtualAddress)) ||
  1216. ((VirtualAddress >= (PVOID) MiSessionViewStart) &&
  1217. (VirtualAddress < (PVOID) MiSessionSpaceWs)))) {
  1218. ZeroPageNeeded = TRUE;
  1219. }
  1220. }
  1221. PageColor = 0xFFFFFFFF;
  1222. }
  1223. if (OldIrql == MM_NOIRQL) {
  1224. CallerHeldPfn = FALSE;
  1225. LOCK_PFN (OldIrql);
  1226. }
  1227. MM_PFN_LOCK_ASSERT();
  1228. ASSERT (PointerPte->u.Hard.Valid == 0);
  1229. //
  1230. // Check to see if a page is available, if a wait is
  1231. // returned, do not continue, just return success.
  1232. //
  1233. if ((MmAvailablePages >= MM_HIGH_LIMIT) ||
  1234. (!MiEnsureAvailablePageOrWait (Process, VirtualAddress, OldIrql))) {
  1235. if (PageColor != 0xFFFFFFFF) {
  1236. //
  1237. // This page is for a user process and so must be zeroed.
  1238. //
  1239. PageFrameIndex = MiRemoveZeroPageIfAny (PageColor);
  1240. if (PageFrameIndex) {
  1241. //
  1242. // This barrier check is needed after zeroing the page
  1243. // and before setting the PTE valid. Note since the PFN
  1244. // database entry is used to hold the sequence timestamp,
  1245. // it must be captured now. Check it at the last possible
  1246. // moment.
  1247. //
  1248. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1249. BarrierStamp = (ULONG)Pfn1->u4.PteFrame;
  1250. }
  1251. else {
  1252. PageFrameIndex = MiRemoveAnyPage (PageColor);
  1253. NeedToZero = TRUE;
  1254. }
  1255. }
  1256. else {
  1257. //
  1258. // As this is a system page, there is no need to
  1259. // remove a page of zeroes, it must be initialized by
  1260. // the system before being used.
  1261. //
  1262. PageColor = MI_PAGE_COLOR_VA_PROCESS (VirtualAddress,
  1263. &MI_SYSTEM_PAGE_COLOR);
  1264. if (ZeroPageNeeded) {
  1265. PageFrameIndex = MiRemoveZeroPage (PageColor);
  1266. }
  1267. else {
  1268. PageFrameIndex = MiRemoveAnyPage (PageColor);
  1269. }
  1270. }
  1271. MiInitializePfn (PageFrameIndex, PointerPte, 1);
  1272. if (CallerHeldPfn == FALSE) {
  1273. UNLOCK_PFN (OldIrql);
  1274. if (Process > HYDRA_PROCESS) {
  1275. Process->NumberOfPrivatePages += 1;
  1276. BarrierNeeded = TRUE;
  1277. }
  1278. }
  1279. InterlockedIncrement ((PLONG) &MmInfoCounters.DemandZeroCount);
  1280. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1281. if (NeedToZero) {
  1282. MiZeroPhysicalPage (PageFrameIndex, PageColor);
  1283. //
  1284. // Note the stamping must occur after the page is zeroed.
  1285. //
  1286. MI_BARRIER_STAMP_ZEROED_PAGE (&BarrierStamp);
  1287. }
  1288. //
  1289. // As this page is demand zero, set the modified bit in the
  1290. // PFN database element and set the dirty bit in the PTE.
  1291. //
  1292. PERFINFO_SOFTFAULT(Pfn1, VirtualAddress, PERFINFO_LOG_TYPE_DEMANDZEROFAULT)
  1293. MI_SNAP_DATA (Pfn1, PointerPte, 5);
  1294. MI_MAKE_VALID_PTE (TempPte,
  1295. PageFrameIndex,
  1296. PointerPte->u.Soft.Protection,
  1297. PointerPte);
  1298. if (TempPte.u.Hard.Write != 0) {
  1299. MI_SET_PTE_DIRTY (TempPte);
  1300. }
  1301. if (BarrierNeeded) {
  1302. MI_BARRIER_SYNCHRONIZE (BarrierStamp);
  1303. }
  1304. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  1305. if (CallerHeldPfn == FALSE) {
  1306. ASSERT (Pfn1->u1.Event == 0);
  1307. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  1308. WorkingSetIndex = MiAddValidPageToWorkingSet (VirtualAddress,
  1309. PointerPte,
  1310. Pfn1,
  1311. 0);
  1312. if (WorkingSetIndex == 0) {
  1313. //
  1314. // Trim the page since we couldn't add it to the working
  1315. // set list at this time.
  1316. //
  1317. MiTrimPte (VirtualAddress,
  1318. PointerPte,
  1319. Pfn1,
  1320. Process,
  1321. ZeroPte);
  1322. return STATUS_NO_MEMORY;
  1323. }
  1324. }
  1325. return STATUS_PAGE_FAULT_DEMAND_ZERO;
  1326. }
  1327. if (CallerHeldPfn == FALSE) {
  1328. UNLOCK_PFN (OldIrql);
  1329. }
  1330. return STATUS_REFAULT;
  1331. }
  1332. NTSTATUS
  1333. MiResolveTransitionFault (
  1334. IN PVOID FaultingAddress,
  1335. IN PMMPTE PointerPte,
  1336. IN PEPROCESS CurrentProcess,
  1337. IN KIRQL OldIrql,
  1338. OUT PLOGICAL ApcNeeded,
  1339. OUT PMMINPAGE_SUPPORT *InPageBlock
  1340. )
  1341. /*++
  1342. Routine Description:
  1343. This routine resolves a transition page fault.
  1344. Arguments:
  1345. FaultingAddress - Supplies the faulting address.
  1346. PointerPte - Supplies the PTE for the faulting address.
  1347. CurrentProcess - Supplies a pointer to the process object. If this
  1348. parameter is NULL, then the fault is for system
  1349. space and the process's working set lock is not held.
  1350. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  1351. ApcNeeded - Supplies a pointer to a location set to TRUE if an I/O
  1352. completion APC is needed to complete partial IRPs that
  1353. collided.
  1354. It is the caller's responsibility to initialize this (usually
  1355. to FALSE) on entry. However, since this routine may be called
  1356. multiple times for a single fault (for the page directory,
  1357. page table and the page itself), it is possible for it to
  1358. occasionally be TRUE on entry.
  1359. If it is FALSE on exit, no completion APC is needed.
  1360. InPageBlock - Supplies a pointer to an inpage block pointer. The caller
  1361. must initialize this to NULL on entry. This routine
  1362. sets this to a non-NULL value to signify an inpage block
  1363. the caller must free when the caller releases the PFN lock.
  1364. Return Value:
  1365. status, either STATUS_SUCCESS, STATUS_REFAULT or an I/O status
  1366. code.
  1367. Environment:
  1368. Kernel mode, PFN lock may optionally be held.
  1369. --*/
  1370. {
  1371. MMPFNENTRY PfnFlags;
  1372. PFN_NUMBER PageFrameIndex;
  1373. PMMPFN Pfn1;
  1374. PMMPFN Pfn2;
  1375. MMPTE TempPte;
  1376. MMPTE TempPte2;
  1377. NTSTATUS status;
  1378. NTSTATUS PfnStatus;
  1379. PMMINPAGE_SUPPORT CapturedEvent;
  1380. PETHREAD CurrentThread;
  1381. PMMPTE PointerToPteForProtoPage;
  1382. WSLE_NUMBER WorkingSetIndex;
  1383. ULONG PfnLockHeld;
  1384. //
  1385. // ***********************************************************
  1386. // Transition PTE.
  1387. // ***********************************************************
  1388. //
  1389. //
  1390. // A transition PTE is either on the free or modified list,
  1391. // on neither list because of its ReferenceCount
  1392. // or currently being read in from the disk (read in progress).
  1393. // If the page is read in progress, this is a collided page
  1394. // and must be handled accordingly.
  1395. //
  1396. ASSERT (*InPageBlock == NULL);
  1397. if (OldIrql == MM_NOIRQL) {
  1398. PfnLockHeld = FALSE;
  1399. //
  1400. // Read the PTE now without the PFN lock so that the PFN entry
  1401. // calculations, etc can be done in advance. If it turns out the PTE
  1402. // changed after the lock is acquired (should be rare), then
  1403. // recalculate.
  1404. //
  1405. TempPte2 = *PointerPte;
  1406. PageFrameIndex = (PFN_NUMBER) TempPte2.u.Hard.PageFrameNumber;
  1407. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1408. ASSERT (OldIrql == MM_NOIRQL);
  1409. LOCK_PFN (OldIrql);
  1410. TempPte = *PointerPte;
  1411. if ((TempPte.u.Soft.Valid == 0) &&
  1412. (TempPte.u.Soft.Prototype == 0) &&
  1413. (TempPte.u.Soft.Transition == 1)) {
  1414. if (TempPte2.u.Long != TempPte.u.Long) {
  1415. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&TempPte);
  1416. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1417. }
  1418. NOTHING;
  1419. }
  1420. else {
  1421. UNLOCK_PFN (OldIrql);
  1422. return STATUS_REFAULT;
  1423. }
  1424. }
  1425. else {
  1426. PfnLockHeld = TRUE;
  1427. ASSERT (OldIrql != MM_NOIRQL);
  1428. TempPte = *PointerPte;
  1429. ASSERT ((TempPte.u.Soft.Valid == 0) &&
  1430. (TempPte.u.Soft.Prototype == 0) &&
  1431. (TempPte.u.Soft.Transition == 1));
  1432. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&TempPte);
  1433. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1434. }
  1435. //
  1436. // Still in transition format.
  1437. //
  1438. InterlockedIncrement ((PLONG) &MmInfoCounters.TransitionCount);
  1439. if (Pfn1->u4.InPageError) {
  1440. //
  1441. // There was an in-page read error and there are other
  1442. // threads colliding for this page, delay to let the
  1443. // other threads complete and return. Snap relevant PFN fields
  1444. // before releasing the lock as the page may immediately get
  1445. // reused.
  1446. //
  1447. PfnFlags = Pfn1->u3.e1;
  1448. status = Pfn1->u1.ReadStatus;
  1449. if (!PfnLockHeld) {
  1450. UNLOCK_PFN (OldIrql);
  1451. }
  1452. if (PfnFlags.ReadInProgress) {
  1453. //
  1454. // This only occurs when the page is being reclaimed by the
  1455. // compression reaper. In this case, the page is still on the
  1456. // transition list (so the ReadStatus is really a flink) so
  1457. // substitute a retry status which will induce a delay so the
  1458. // compression reaper can finish taking the page (and PTE).
  1459. //
  1460. return STATUS_NO_MEMORY;
  1461. }
  1462. ASSERT (!NT_SUCCESS(status));
  1463. return status;
  1464. }
  1465. if (Pfn1->u3.e1.ReadInProgress) {
  1466. //
  1467. // Collided page fault.
  1468. //
  1469. #if DBG
  1470. if (MmDebug & MM_DBG_COLLIDED_PAGE) {
  1471. DbgPrint("MM:collided page fault\n");
  1472. }
  1473. #endif
  1474. CapturedEvent = (PMMINPAGE_SUPPORT)Pfn1->u1.Event;
  1475. CurrentThread = PsGetCurrentThread ();
  1476. if (CapturedEvent->Thread == CurrentThread) {
  1477. //
  1478. // This detects when the Io APC completion routine accesses
  1479. // the same user page (ie: during an overlapped I/O) that
  1480. // the user thread has already faulted on.
  1481. //
  1482. // This can result in a fatal deadlock and so must
  1483. // be detected here. Return a unique status code so the
  1484. // (legitimate) callers know this has happened so it can be
  1485. // handled properly, ie: Io must request a callback from
  1486. // the Mm once the first fault has completed.
  1487. //
  1488. // Note that non-legitimate callers must get back a failure
  1489. // status so the thread can be terminated.
  1490. //
  1491. ASSERT ((CurrentThread->NestedFaultCount == 1) ||
  1492. (CurrentThread->NestedFaultCount == 2));
  1493. CurrentThread->ApcNeeded = 1;
  1494. if (!PfnLockHeld) {
  1495. UNLOCK_PFN (OldIrql);
  1496. }
  1497. return STATUS_MULTIPLE_FAULT_VIOLATION;
  1498. }
  1499. //
  1500. // Increment the reference count for the page so it won't be
  1501. // reused until all collisions have been completed.
  1502. //
  1503. ASSERT (Pfn1->u2.ShareCount == 0);
  1504. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  1505. ASSERT (Pfn1->u4.LockCharged == 1);
  1506. Pfn1->u3.e2.ReferenceCount += 1;
  1507. //
  1508. // Careful synchronization is applied to the WaitCount field so
  1509. // that freeing of the inpage block can occur lock-free. Note
  1510. // that the ReadInProgress bit on each PFN is set and cleared while
  1511. // holding the PFN lock. Inpage blocks are always (and must be)
  1512. // freed _AFTER_ the ReadInProgress bit is cleared.
  1513. //
  1514. InterlockedIncrement(&CapturedEvent->WaitCount);
  1515. UNLOCK_PFN (OldIrql);
  1516. if (CurrentProcess == HYDRA_PROCESS) {
  1517. UNLOCK_WORKING_SET (&MmSessionSpace->GlobalVirtualAddress->Vm);
  1518. CurrentThread = NULL;
  1519. }
  1520. else if (CurrentProcess != NULL) {
  1521. //
  1522. // APCs must be explicitly disabled to prevent suspend APCs from
  1523. // interrupting this thread before the wait has been issued.
  1524. // Otherwise the APC can result in this page being locked
  1525. // indefinitely until the suspend is released.
  1526. //
  1527. ASSERT (CurrentThread->NestedFaultCount <= 2);
  1528. CurrentThread->NestedFaultCount += 1;
  1529. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1530. UNLOCK_WS (CurrentProcess);
  1531. }
  1532. else {
  1533. UNLOCK_SYSTEM_WS ();
  1534. CurrentThread = NULL;
  1535. }
  1536. //
  1537. // Set the inpage block address as the waitcount was incremented
  1538. // above and therefore the free must be done by our caller.
  1539. //
  1540. *InPageBlock = CapturedEvent;
  1541. status = MiWaitForInPageComplete (Pfn1,
  1542. PointerPte,
  1543. FaultingAddress,
  1544. &TempPte,
  1545. CapturedEvent,
  1546. CurrentProcess);
  1547. //
  1548. // MiWaitForInPageComplete RETURNS WITH THE WORKING SET LOCK
  1549. // AND PFN LOCK HELD!!!
  1550. //
  1551. if (CurrentThread != NULL) {
  1552. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1553. ASSERT (CurrentThread->NestedFaultCount <= 3);
  1554. ASSERT (CurrentThread->NestedFaultCount != 0);
  1555. CurrentThread->NestedFaultCount -= 1;
  1556. if ((CurrentThread->ApcNeeded == 1) &&
  1557. (CurrentThread->NestedFaultCount == 0)) {
  1558. *ApcNeeded = TRUE;
  1559. CurrentThread->ApcNeeded = 0;
  1560. }
  1561. }
  1562. ASSERT (Pfn1->u3.e1.ReadInProgress == 0);
  1563. if (status != STATUS_SUCCESS) {
  1564. PfnStatus = Pfn1->u1.ReadStatus;
  1565. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(Pfn1, 9);
  1566. //
  1567. // Check to see if an I/O error occurred on this page.
  1568. // If so, try to free the physical page, wait a
  1569. // half second and return a status of PTE_CHANGED.
  1570. // This will result in success being returned to
  1571. // the user and the fault will occur again and should
  1572. // not be a transition fault this time.
  1573. //
  1574. if (Pfn1->u4.InPageError == 1) {
  1575. ASSERT (!NT_SUCCESS(PfnStatus));
  1576. status = PfnStatus;
  1577. if (Pfn1->u3.e2.ReferenceCount == 0) {
  1578. Pfn1->u4.InPageError = 0;
  1579. //
  1580. // Only restore the transition PTE if the address
  1581. // space still exists. Another thread may have
  1582. // deleted the VAD while this thread waited for the
  1583. // fault to complete - in this case, the frame
  1584. // will be marked as free already.
  1585. //
  1586. if (Pfn1->u3.e1.PageLocation != FreePageList) {
  1587. ASSERT (Pfn1->u3.e1.PageLocation ==
  1588. StandbyPageList);
  1589. MiUnlinkPageFromList (Pfn1);
  1590. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1591. MiRestoreTransitionPte (Pfn1);
  1592. MiInsertPageInFreeList (PageFrameIndex);
  1593. }
  1594. }
  1595. }
  1596. #if DBG
  1597. if (MmDebug & MM_DBG_COLLIDED_PAGE) {
  1598. DbgPrint("MM:decrement ref count - PTE changed\n");
  1599. MiFormatPfn(Pfn1);
  1600. }
  1601. #endif
  1602. if (!PfnLockHeld) {
  1603. UNLOCK_PFN (OldIrql);
  1604. }
  1605. //
  1606. // Instead of returning status, always return STATUS_REFAULT.
  1607. // This is to support filesystems that save state in the
  1608. // ETHREAD of the thread that serviced the fault ! Since
  1609. // collided threads never enter the filesystem, their ETHREADs
  1610. // haven't been hacked up. Since this only matters when
  1611. // errors occur (specifically STATUS_VERIFY_REQUIRED today),
  1612. // retry any failed I/O in the context of each collider
  1613. // to give the filesystems ample opportunity.
  1614. //
  1615. return STATUS_REFAULT;
  1616. }
  1617. }
  1618. else {
  1619. //
  1620. // PTE refers to a normal transition PTE.
  1621. //
  1622. ASSERT ((SPFN_NUMBER)MmAvailablePages >= 0);
  1623. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  1624. //
  1625. // Check available pages so that a machine which is low on memory
  1626. // can stop this thread from gobbling up the pages from every modified
  1627. // write that completes because that would starve waiting threads.
  1628. //
  1629. // Another scenario is if the system is utilizing a hardware
  1630. // compression cache. Checking ensures that only a safe amount
  1631. // of the compressed virtual cache is directly mapped so that
  1632. // if the hardware gets into trouble, we can bail it out.
  1633. //
  1634. if ((MmAvailablePages < MM_HIGH_LIMIT) &&
  1635. ((MmAvailablePages == 0) ||
  1636. (PsGetCurrentThread()->MemoryMaker == 0) &&
  1637. (MiEnsureAvailablePageOrWait (CurrentProcess, FaultingAddress, OldIrql)))) {
  1638. //
  1639. // A wait operation was performed which dropped the locks,
  1640. // repeat this fault.
  1641. //
  1642. if (!PfnLockHeld) {
  1643. UNLOCK_PFN (OldIrql);
  1644. }
  1645. //
  1646. // Note our caller will delay execution after releasing the
  1647. // working set mutex in order to make pages available.
  1648. //
  1649. return STATUS_NO_MEMORY;
  1650. }
  1651. ASSERT (Pfn1->u4.InPageError == 0);
  1652. if (Pfn1->u3.e1.PageLocation == ActiveAndValid) {
  1653. //
  1654. // This page must contain an MmSt allocation of prototype PTEs.
  1655. // Because these types of pages reside in paged pool (or special
  1656. // pool) and are part of the system working set, they can be
  1657. // trimmed at any time regardless of the share count. However,
  1658. // if the share count is nonzero, then the page state will
  1659. // remain active and the page will remain in memory - but the
  1660. // PTE will be set to the transition state. Make the page
  1661. // valid without incrementing the reference count, but
  1662. // increment the share count.
  1663. //
  1664. ASSERT (((Pfn1->PteAddress >= MiGetPteAddress(MmPagedPoolStart)) &&
  1665. (Pfn1->PteAddress <= MiGetPteAddress(MmPagedPoolEnd))) ||
  1666. ((Pfn1->PteAddress >= MiGetPteAddress(MmSpecialPoolStart)) &&
  1667. (Pfn1->PteAddress <= MiGetPteAddress(MmSpecialPoolEnd))));
  1668. //
  1669. // Don't increment the valid PTE count for the
  1670. // page table page.
  1671. //
  1672. ASSERT (Pfn1->u2.ShareCount != 0);
  1673. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  1674. }
  1675. else {
  1676. MiUnlinkPageFromList (Pfn1);
  1677. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  1678. //
  1679. // Update the PFN database - the reference count must be
  1680. // incremented as the share count is going to go from zero to 1.
  1681. //
  1682. ASSERT (Pfn1->u2.ShareCount == 0);
  1683. //
  1684. // The PFN reference count will be 1 already here if the
  1685. // modified writer has begun a write of this page. Otherwise
  1686. // it's ordinarily 0.
  1687. //
  1688. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, TRUE, 8);
  1689. Pfn1->u3.e2.ReferenceCount += 1;
  1690. }
  1691. }
  1692. //
  1693. // Join with collided page fault code to handle updating
  1694. // the transition PTE.
  1695. //
  1696. ASSERT (Pfn1->u4.InPageError == 0);
  1697. if (Pfn1->u2.ShareCount == 0) {
  1698. MI_REMOVE_LOCKED_PAGE_CHARGE (Pfn1, 9);
  1699. }
  1700. Pfn1->u2.ShareCount += 1;
  1701. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1702. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  1703. //
  1704. // Paged pool is trimmed without regard to sharecounts.
  1705. // This means a paged pool PTE can be in transition while
  1706. // the page is still marked active.
  1707. //
  1708. // Note this check only needs to be done for system space addresses
  1709. // as user space address faults lock down the page containing the
  1710. // prototype PTE entries before processing the fault.
  1711. //
  1712. // One example is a system cache fault - the FaultingAddress is a
  1713. // system cache virtual address, the PointerPte points at the pool
  1714. // allocation containing the relevant prototype PTEs. This page
  1715. // may have been trimmed because it isn't locked down during
  1716. // processing of system space virtual address faults.
  1717. //
  1718. if (FaultingAddress >= MmSystemRangeStart) {
  1719. PointerToPteForProtoPage = MiGetPteAddress (PointerPte);
  1720. TempPte = *PointerToPteForProtoPage;
  1721. if ((TempPte.u.Hard.Valid == 0) &&
  1722. (TempPte.u.Soft.Transition == 1)) {
  1723. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&TempPte);
  1724. Pfn2 = MI_PFN_ELEMENT (PageFrameIndex);
  1725. ASSERT ((Pfn2->u3.e1.ReadInProgress == 0) &&
  1726. (Pfn2->u4.InPageError));
  1727. ASSERT (Pfn2->u3.e1.PageLocation == ActiveAndValid);
  1728. ASSERT (((Pfn2->PteAddress >= MiGetPteAddress(MmPagedPoolStart)) &&
  1729. (Pfn2->PteAddress <= MiGetPteAddress(MmPagedPoolEnd))) ||
  1730. ((Pfn2->PteAddress >= MiGetPteAddress(MmSpecialPoolStart)) &&
  1731. (Pfn2->PteAddress <= MiGetPteAddress(MmSpecialPoolEnd))));
  1732. //
  1733. // Don't increment the valid PTE count for the
  1734. // paged pool page.
  1735. //
  1736. ASSERT (Pfn2->u2.ShareCount != 0);
  1737. ASSERT (Pfn2->u3.e2.ReferenceCount != 0);
  1738. ASSERT (Pfn2->u3.e1.CacheAttribute == MiCached);
  1739. MI_MAKE_VALID_PTE (TempPte,
  1740. PageFrameIndex,
  1741. Pfn2->OriginalPte.u.Soft.Protection,
  1742. PointerToPteForProtoPage);
  1743. MI_WRITE_VALID_PTE (PointerToPteForProtoPage, TempPte);
  1744. }
  1745. }
  1746. MI_MAKE_TRANSITION_PTE_VALID (TempPte, PointerPte);
  1747. //
  1748. // If the modified field is set in the PFN database and this
  1749. // page is not copy on modify, then set the dirty bit.
  1750. // This can be done as the modified page will not be
  1751. // written to the paging file until this PTE is made invalid.
  1752. //
  1753. if ((Pfn1->u3.e1.Modified && TempPte.u.Hard.Write) &&
  1754. (TempPte.u.Hard.CopyOnWrite == 0)) {
  1755. MI_SET_PTE_DIRTY (TempPte);
  1756. }
  1757. else {
  1758. MI_SET_PTE_CLEAN (TempPte);
  1759. }
  1760. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  1761. if (!PfnLockHeld) {
  1762. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  1763. UNLOCK_PFN (OldIrql);
  1764. PERFINFO_SOFTFAULT(Pfn1, FaultingAddress, PERFINFO_LOG_TYPE_TRANSITIONFAULT)
  1765. WorkingSetIndex = MiAddValidPageToWorkingSet (FaultingAddress,
  1766. PointerPte,
  1767. Pfn1,
  1768. 0);
  1769. if (WorkingSetIndex == 0) {
  1770. //
  1771. // Trim the page since we couldn't add it to the working
  1772. // set list at this time.
  1773. //
  1774. MiTrimPte (FaultingAddress,
  1775. PointerPte,
  1776. Pfn1,
  1777. CurrentProcess,
  1778. ZeroPte);
  1779. return STATUS_NO_MEMORY;
  1780. }
  1781. }
  1782. return STATUS_PAGE_FAULT_TRANSITION;
  1783. }
  1784. NTSTATUS
  1785. MiResolvePageFileFault (
  1786. IN PVOID FaultingAddress,
  1787. IN PMMPTE PointerPte,
  1788. OUT PMMINPAGE_SUPPORT *ReadBlock,
  1789. IN PEPROCESS Process,
  1790. IN KIRQL OldIrql
  1791. )
  1792. /*++
  1793. Routine Description:
  1794. This routine builds the MDL and other structures to allow a
  1795. read operation on a page file for a page fault.
  1796. Arguments:
  1797. FaultingAddress - Supplies the faulting address.
  1798. PointerPte - Supplies the PTE for the faulting address.
  1799. ReadBlock - Supplies a pointer to put the address of the read block which
  1800. needs to be completed before an I/O can be issued.
  1801. Process - Supplies a pointer to the process object. If this
  1802. parameter is NULL, then the fault is for system
  1803. space and the process's working set lock is not held.
  1804. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  1805. Return Value:
  1806. status. A status value of STATUS_ISSUE_PAGING_IO is returned
  1807. if this function completes successfully.
  1808. Environment:
  1809. Kernel mode, PFN lock held.
  1810. --*/
  1811. {
  1812. PMDL Mdl;
  1813. ULONG i;
  1814. PMMPTE BasePte;
  1815. PMMPTE CheckPte;
  1816. PMMPTE FirstPte;
  1817. PMMPTE LastPte;
  1818. PSUBSECTION Subsection;
  1819. ULONG ReadSize;
  1820. LARGE_INTEGER StartingOffset;
  1821. PFN_NUMBER PageFrameIndex;
  1822. PPFN_NUMBER MdlPage;
  1823. ULONG PageFileNumber;
  1824. ULONG ClusterSize;
  1825. ULONG BackwardPageCount;
  1826. ULONG ForwardPageCount;
  1827. ULONG MaxForwardPageCount;
  1828. ULONG MaxBackwardPageCount;
  1829. WSLE_NUMBER WorkingSetIndex;
  1830. ULONG PageColor;
  1831. MMPTE TempPte;
  1832. MMPTE ComparePte;
  1833. PMMINPAGE_SUPPORT ReadBlockLocal;
  1834. PETHREAD CurrentThread;
  1835. PMMVAD Vad;
  1836. NTSTATUS Status;
  1837. // **************************************************
  1838. // Page File Read
  1839. // **************************************************
  1840. //
  1841. // Calculate the VBN for the in-page operation.
  1842. //
  1843. TempPte = *PointerPte;
  1844. ASSERT (TempPte.u.Hard.Valid == 0);
  1845. ASSERT (TempPte.u.Soft.Prototype == 0);
  1846. ASSERT (TempPte.u.Soft.Transition == 0);
  1847. MM_PFN_LOCK_ASSERT();
  1848. if ((MmAvailablePages < MM_HIGH_LIMIT) &&
  1849. (MiEnsureAvailablePageOrWait (Process, FaultingAddress, OldIrql))) {
  1850. //
  1851. // A wait operation was performed which dropped the locks,
  1852. // repeat this fault.
  1853. //
  1854. UNLOCK_PFN (OldIrql);
  1855. return STATUS_REFAULT;
  1856. }
  1857. ReadBlockLocal = MiGetInPageSupportBlock (OldIrql, &Status);
  1858. if (ReadBlockLocal == NULL) {
  1859. UNLOCK_PFN (OldIrql);
  1860. ASSERT (!NT_SUCCESS (Status));
  1861. return Status;
  1862. }
  1863. //
  1864. // Transition collisions rely on the entire PFN (including the event field)
  1865. // being initialized, the ReadBlockLocal's event being not-signaled,
  1866. // and the ReadBlockLocal's thread and waitcount being initialized.
  1867. //
  1868. // All of this has been done by MiGetInPageSupportBlock already except
  1869. // the PFN settings. The PFN lock can be safely released once
  1870. // this is done.
  1871. //
  1872. ReadSize = 1;
  1873. BasePte = NULL;
  1874. if (MI_IS_PAGE_TABLE_ADDRESS(PointerPte)) {
  1875. WorkingSetIndex = 1;
  1876. }
  1877. else {
  1878. WorkingSetIndex = MI_PROTOTYPE_WSINDEX;
  1879. }
  1880. //
  1881. // Capture the desired cluster size.
  1882. //
  1883. ClusterSize = MmClusterPageFileReads;
  1884. ASSERT (ClusterSize <= MM_MAXIMUM_READ_CLUSTER_SIZE);
  1885. if (MiInPageSinglePages != 0) {
  1886. MiInPageSinglePages -= 1;
  1887. }
  1888. else if ((ClusterSize > 1) && (MmAvailablePages > MM_PLENTY_FREE_LIMIT)) {
  1889. //
  1890. // Maybe this condition should be only on free+zeroed pages (ie: don't
  1891. // include standby). Maybe it should look at the recycle rate of
  1892. // the standby list, etc, etc.
  1893. //
  1894. ASSERT (ClusterSize <= MmAvailablePages);
  1895. //
  1896. // Attempt to cluster ahead and behind.
  1897. //
  1898. MaxForwardPageCount = PTE_PER_PAGE - (BYTE_OFFSET (PointerPte) / sizeof (MMPTE));
  1899. ASSERT (MaxForwardPageCount != 0);
  1900. MaxBackwardPageCount = PTE_PER_PAGE - MaxForwardPageCount;
  1901. MaxForwardPageCount -= 1;
  1902. if (WorkingSetIndex == MI_PROTOTYPE_WSINDEX) {
  1903. //
  1904. // This is a pagefile read for a shared memory (prototype PTE)
  1905. // backed section. Stay within the prototype PTE pool allocation.
  1906. //
  1907. // The prototype PTE pool start and end must be carefully
  1908. // calculated (remember the user's view may be smaller or larger
  1909. // than this). Don't bother walking the entire VAD tree if it is
  1910. // very large as this can take a significant amount of time.
  1911. //
  1912. if ((FaultingAddress <= MM_HIGHEST_USER_ADDRESS) &&
  1913. (Process->VadRoot.NumberGenericTableElements < 128)) {
  1914. Vad = MiLocateAddress (FaultingAddress);
  1915. if (Vad != NULL) {
  1916. Subsection = MiLocateSubsection (Vad,
  1917. MI_VA_TO_VPN(FaultingAddress));
  1918. if (Subsection != NULL) {
  1919. FirstPte = &Subsection->SubsectionBase[0];
  1920. LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  1921. if ((ULONG)(LastPte - PointerPte - 1) < MaxForwardPageCount) {
  1922. MaxForwardPageCount = (ULONG)(LastPte - PointerPte - 1);
  1923. }
  1924. if ((ULONG)(PointerPte - FirstPte) < MaxBackwardPageCount) {
  1925. MaxBackwardPageCount = (ULONG)(PointerPte - FirstPte);
  1926. }
  1927. }
  1928. else {
  1929. ClusterSize = 0;
  1930. }
  1931. }
  1932. else {
  1933. ClusterSize = 0;
  1934. }
  1935. }
  1936. else {
  1937. ClusterSize = 0;
  1938. }
  1939. }
  1940. CurrentThread = PsGetCurrentThread();
  1941. if (CurrentThread->ForwardClusterOnly) {
  1942. MaxBackwardPageCount = 0;
  1943. if (MaxForwardPageCount == 0) {
  1944. //
  1945. // This PTE is the last one in the page table page and
  1946. // no backwards clustering is enabled for this thread so
  1947. // no clustering can be done.
  1948. //
  1949. ClusterSize = 0;
  1950. }
  1951. }
  1952. if (ClusterSize != 0) {
  1953. if (MaxForwardPageCount > ClusterSize) {
  1954. MaxForwardPageCount = ClusterSize;
  1955. }
  1956. ComparePte = TempPte;
  1957. CheckPte = PointerPte + 1;
  1958. ForwardPageCount = MaxForwardPageCount;
  1959. //
  1960. // Try to cluster forward within the page of PTEs.
  1961. //
  1962. while (ForwardPageCount != 0) {
  1963. ASSERT (MiIsPteOnPdeBoundary (CheckPte) == 0);
  1964. ComparePte.u.Soft.PageFileHigh += 1;
  1965. if (CheckPte->u.Long != ComparePte.u.Long) {
  1966. break;
  1967. }
  1968. ForwardPageCount -= 1;
  1969. CheckPte += 1;
  1970. }
  1971. ReadSize += (MaxForwardPageCount - ForwardPageCount);
  1972. //
  1973. // Try to cluster backward within the page of PTEs. Donate
  1974. // any unused forward cluster space to the backwards gathering
  1975. // but keep the entire transfer within the MDL.
  1976. //
  1977. ClusterSize -= (MaxForwardPageCount - ForwardPageCount);
  1978. if (MaxBackwardPageCount > ClusterSize) {
  1979. MaxBackwardPageCount = ClusterSize;
  1980. }
  1981. ComparePte = TempPte;
  1982. BasePte = PointerPte;
  1983. CheckPte = PointerPte;
  1984. BackwardPageCount = MaxBackwardPageCount;
  1985. while (BackwardPageCount != 0) {
  1986. ASSERT (MiIsPteOnPdeBoundary(CheckPte) == 0);
  1987. CheckPte -= 1;
  1988. ComparePte.u.Soft.PageFileHigh -= 1;
  1989. if (CheckPte->u.Long != ComparePte.u.Long) {
  1990. break;
  1991. }
  1992. BackwardPageCount -= 1;
  1993. }
  1994. ReadSize += (MaxBackwardPageCount - BackwardPageCount);
  1995. BasePte -= (MaxBackwardPageCount - BackwardPageCount);
  1996. }
  1997. }
  1998. if (ReadSize == 1) {
  1999. //
  2000. // Get a page and put the PTE into the transition state with the
  2001. // read-in-progress flag set.
  2002. //
  2003. if (Process == HYDRA_PROCESS) {
  2004. PageColor = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
  2005. }
  2006. else if (Process == NULL) {
  2007. PageColor = MI_GET_PAGE_COLOR_FROM_VA(FaultingAddress);
  2008. }
  2009. else {
  2010. PageColor = MI_PAGE_COLOR_VA_PROCESS (FaultingAddress,
  2011. &Process->NextPageColor);
  2012. }
  2013. PageFrameIndex = MiRemoveAnyPage (PageColor);
  2014. MiInitializeReadInProgressSinglePfn (PageFrameIndex,
  2015. PointerPte,
  2016. &ReadBlockLocal->Event,
  2017. WorkingSetIndex);
  2018. MI_RETRIEVE_USED_PAGETABLE_ENTRIES_FROM_PTE (ReadBlockLocal, &TempPte);
  2019. }
  2020. else {
  2021. Mdl = &ReadBlockLocal->Mdl;
  2022. MdlPage = &ReadBlockLocal->Page[0];
  2023. ASSERT (ReadSize <= MmAvailablePages);
  2024. for (i = 0; i < ReadSize; i += 1) {
  2025. //
  2026. // Get a page and put the PTE into the transition state with the
  2027. // read-in-progress flag set.
  2028. //
  2029. if (Process == HYDRA_PROCESS) {
  2030. PageColor = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
  2031. }
  2032. else if (Process == NULL) {
  2033. PageColor = MI_GET_PAGE_COLOR_FROM_VA(FaultingAddress);
  2034. }
  2035. else {
  2036. PageColor = MI_PAGE_COLOR_VA_PROCESS (FaultingAddress,
  2037. &Process->NextPageColor);
  2038. }
  2039. *MdlPage = MiRemoveAnyPage (PageColor);
  2040. MdlPage += 1;
  2041. }
  2042. ReadSize *= PAGE_SIZE;
  2043. //
  2044. // Note PageFrameIndex is the actual frame that was requested by
  2045. // this caller. All the other frames will be put in transition
  2046. // when the inpage completes (provided there are no colliding threads).
  2047. //
  2048. MdlPage = &ReadBlockLocal->Page[0];
  2049. PageFrameIndex = *(MdlPage + (PointerPte - BasePte));
  2050. //
  2051. // Initialize the MDL for this request.
  2052. //
  2053. MmInitializeMdl (Mdl,
  2054. MiGetVirtualAddressMappedByPte (BasePte),
  2055. ReadSize);
  2056. Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
  2057. //
  2058. // Set PointerPte and TempPte to the base of the cluster so the
  2059. // correct starting offset can be calculated below. Note this must
  2060. // be done before MiInitializeReadInProgressPfn overwrites the PTEs.
  2061. //
  2062. PointerPte = BasePte;
  2063. TempPte = *PointerPte;
  2064. ASSERT (TempPte.u.Soft.Prototype == 0);
  2065. ASSERT (TempPte.u.Soft.Transition == 0);
  2066. //
  2067. // Put the PTEs into the transition state with the
  2068. // read-in-progress flag set.
  2069. //
  2070. MiInitializeReadInProgressPfn (Mdl,
  2071. BasePte,
  2072. &ReadBlockLocal->Event,
  2073. WorkingSetIndex);
  2074. MI_ZERO_USED_PAGETABLE_ENTRIES_IN_INPAGE_SUPPORT(ReadBlockLocal);
  2075. }
  2076. UNLOCK_PFN (OldIrql);
  2077. InterlockedIncrement ((PLONG) &MmInfoCounters.PageReadCount);
  2078. InterlockedIncrement ((PLONG) &MmInfoCounters.PageReadIoCount);
  2079. *ReadBlock = ReadBlockLocal;
  2080. PageFileNumber = GET_PAGING_FILE_NUMBER (TempPte);
  2081. StartingOffset.LowPart = GET_PAGING_FILE_OFFSET (TempPte);
  2082. ASSERT (StartingOffset.LowPart <= MmPagingFile[PageFileNumber]->Size);
  2083. StartingOffset.HighPart = 0;
  2084. StartingOffset.QuadPart = StartingOffset.QuadPart << PAGE_SHIFT;
  2085. ReadBlockLocal->FilePointer = MmPagingFile[PageFileNumber]->File;
  2086. #if DBG
  2087. if (((StartingOffset.QuadPart >> PAGE_SHIFT) < 8192) && (PageFileNumber == 0)) {
  2088. if ((MmPagingFileDebug[StartingOffset.QuadPart >> PAGE_SHIFT] & ~0x1f) !=
  2089. ((ULONG_PTR)PointerPte << 3)) {
  2090. if ((MmPagingFileDebug[StartingOffset.QuadPart >> PAGE_SHIFT] & ~0x1f) !=
  2091. ((ULONG_PTR)(MiGetPteAddress(FaultingAddress)) << 3)) {
  2092. DbgPrint("MMINPAGE: Mismatch PointerPte %p Offset %I64X info %p\n",
  2093. PointerPte,
  2094. StartingOffset.QuadPart >> PAGE_SHIFT,
  2095. MmPagingFileDebug[StartingOffset.QuadPart >> PAGE_SHIFT]);
  2096. DbgBreakPoint();
  2097. }
  2098. }
  2099. }
  2100. #endif //DBG
  2101. ReadBlockLocal->ReadOffset = StartingOffset;
  2102. ReadBlockLocal->BasePte = PointerPte;
  2103. //
  2104. // Build a single page MDL for the request unless it was a cluster -
  2105. // clustered MDLs have already been constructed.
  2106. //
  2107. if (ReadSize == 1) {
  2108. MmInitializeMdl (&ReadBlockLocal->Mdl, PAGE_ALIGN(FaultingAddress), PAGE_SIZE);
  2109. ReadBlockLocal->Mdl.MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
  2110. ReadBlockLocal->Page[0] = PageFrameIndex;
  2111. }
  2112. ReadBlockLocal->Pfn = MI_PFN_ELEMENT (PageFrameIndex);
  2113. return STATUS_ISSUE_PAGING_IO;
  2114. }
  2115. NTSTATUS
  2116. MiResolveProtoPteFault (
  2117. IN ULONG_PTR StoreInstruction,
  2118. IN PVOID FaultingAddress,
  2119. IN PMMPTE PointerPte,
  2120. IN PMMPTE PointerProtoPte,
  2121. IN OUT PMMPFN *LockedProtoPfn,
  2122. OUT PMMINPAGE_SUPPORT *ReadBlock,
  2123. IN PEPROCESS Process,
  2124. IN KIRQL OldIrql,
  2125. OUT PLOGICAL ApcNeeded
  2126. )
  2127. /*++
  2128. Routine Description:
  2129. This routine resolves a prototype PTE fault.
  2130. Arguments:
  2131. StoreInstruction - Supplies nonzero if the instruction is trying
  2132. to modify the faulting address (i.e. write
  2133. access required).
  2134. FaultingAddress - Supplies the faulting address.
  2135. PointerPte - Supplies the PTE for the faulting address.
  2136. PointerProtoPte - Supplies a pointer to the prototype PTE to fault in.
  2137. LockedProtoPfn - Supplies a non-NULL pointer to the prototype PTE's PFN
  2138. that was locked down by the caller, or NULL if the caller
  2139. did not lock down any PFN. This routine may unlock
  2140. the PFN - if so, it must also clear this pointer.
  2141. ReadBlock - Supplies a pointer to put the address of the read block which
  2142. needs to be completed before an I/O can be issued.
  2143. Process - Supplies a pointer to the process object. If this
  2144. parameter is NULL, then the fault is for system
  2145. space and the process's working set lock is not held.
  2146. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  2147. ApcNeeded - Supplies a pointer to a location set to TRUE if an I/O
  2148. completion APC is needed to complete partial IRPs that
  2149. collided.
  2150. Return Value:
  2151. NTSTATUS: STATUS_SUCCESS, STATUS_REFAULT, or an I/O status code.
  2152. Environment:
  2153. Kernel mode, PFN lock held.
  2154. --*/
  2155. {
  2156. MMPTE TempPte;
  2157. PFN_NUMBER PageFrameIndex;
  2158. PMMPFN Pfn1;
  2159. NTSTATUS status;
  2160. ULONG CopyOnWrite;
  2161. LOGICAL PfnHeld;
  2162. PMMINPAGE_SUPPORT CapturedEvent;
  2163. CapturedEvent = NULL;
  2164. //
  2165. // Note the PFN lock must be held as the routine to locate a working
  2166. // set entry decrements the share count of PFN elements.
  2167. //
  2168. MM_PFN_LOCK_ASSERT ();
  2169. ASSERT (PointerPte->u.Soft.Prototype == 1);
  2170. TempPte = *PointerProtoPte;
  2171. //
  2172. // The page containing the prototype PTE is resident,
  2173. // handle the fault referring to the prototype PTE.
  2174. // If the prototype PTE is already valid, make this
  2175. // PTE valid and up the share count etc.
  2176. //
  2177. if (TempPte.u.Hard.Valid) {
  2178. //
  2179. // Prototype PTE is valid.
  2180. //
  2181. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
  2182. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2183. Pfn1->u2.ShareCount += 1;
  2184. status = STATUS_SUCCESS;
  2185. //
  2186. // Count this as a transition fault.
  2187. //
  2188. InterlockedIncrement ((PLONG) &MmInfoCounters.TransitionCount);
  2189. PfnHeld = TRUE;
  2190. PERFINFO_SOFTFAULT(Pfn1, FaultingAddress, PERFINFO_LOG_TYPE_ADDVALIDPAGETOWS)
  2191. }
  2192. else {
  2193. //
  2194. // Check to make sure the prototype PTE is committed.
  2195. //
  2196. if (TempPte.u.Long == 0) {
  2197. MI_BREAK_ON_AV (FaultingAddress, 8);
  2198. UNLOCK_PFN (OldIrql);
  2199. return STATUS_ACCESS_VIOLATION;
  2200. }
  2201. //
  2202. // If the PTE indicates that the protection field to be
  2203. // checked is in the prototype PTE, check it now.
  2204. //
  2205. CopyOnWrite = FALSE;
  2206. if (PointerPte->u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) {
  2207. if (PointerPte->u.Proto.ReadOnly == 0) {
  2208. //
  2209. // Check for kernel mode access, we have already verified
  2210. // that the user has access to the virtual address.
  2211. //
  2212. status = MiAccessCheck (PointerProtoPte,
  2213. StoreInstruction,
  2214. KernelMode,
  2215. MI_GET_PROTECTION_FROM_SOFT_PTE (PointerProtoPte),
  2216. TRUE);
  2217. if (status != STATUS_SUCCESS) {
  2218. if ((StoreInstruction) &&
  2219. (MI_IS_SESSION_ADDRESS (FaultingAddress)) &&
  2220. (MmSessionSpace->ImageLoadingCount != 0)) {
  2221. PLIST_ENTRY NextEntry;
  2222. PIMAGE_ENTRY_IN_SESSION Image;
  2223. NextEntry = MmSessionSpace->ImageList.Flink;
  2224. while (NextEntry != &MmSessionSpace->ImageList) {
  2225. Image = CONTAINING_RECORD (NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  2226. if ((FaultingAddress >= Image->Address) &&
  2227. (FaultingAddress <= Image->LastAddress)) {
  2228. if (Image->ImageLoading) {
  2229. //
  2230. // Temporarily allow the write so that
  2231. // relocations and import snaps can be
  2232. // completed.
  2233. //
  2234. // Even though the page's current backing
  2235. // is the image file, the modified writer
  2236. // will convert it to pagefile backing
  2237. // when it notices the change later.
  2238. //
  2239. goto done;
  2240. }
  2241. break;
  2242. }
  2243. NextEntry = NextEntry->Flink;
  2244. }
  2245. }
  2246. MI_BREAK_ON_AV (FaultingAddress, 9);
  2247. UNLOCK_PFN (OldIrql);
  2248. return status;
  2249. }
  2250. if ((PointerProtoPte->u.Soft.Protection & MM_COPY_ON_WRITE_MASK) ==
  2251. MM_COPY_ON_WRITE_MASK) {
  2252. CopyOnWrite = TRUE;
  2253. }
  2254. }
  2255. done:
  2256. NOTHING;
  2257. }
  2258. else {
  2259. if ((PointerPte->u.Soft.Protection & MM_COPY_ON_WRITE_MASK) ==
  2260. MM_COPY_ON_WRITE_MASK) {
  2261. CopyOnWrite = TRUE;
  2262. }
  2263. }
  2264. if ((!IS_PTE_NOT_DEMAND_ZERO(TempPte)) && (CopyOnWrite)) {
  2265. MMPTE DemandZeroPte;
  2266. //
  2267. // The prototype PTE is demand zero and copy on
  2268. // write. Make this PTE a private demand zero PTE.
  2269. //
  2270. ASSERT (Process != NULL);
  2271. UNLOCK_PFN (OldIrql);
  2272. DemandZeroPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
  2273. MI_WRITE_INVALID_PTE (PointerPte, DemandZeroPte);
  2274. status = MiResolveDemandZeroFault (FaultingAddress,
  2275. PointerPte,
  2276. Process,
  2277. MM_NOIRQL);
  2278. return status;
  2279. }
  2280. //
  2281. // Make the prototype PTE valid, the prototype PTE is in
  2282. // one of these 4 states:
  2283. //
  2284. // demand zero
  2285. // transition
  2286. // paging file
  2287. // mapped file
  2288. //
  2289. if (TempPte.u.Soft.Prototype == 1) {
  2290. //
  2291. // Mapped File.
  2292. //
  2293. status = MiResolveMappedFileFault (FaultingAddress,
  2294. PointerProtoPte,
  2295. ReadBlock,
  2296. Process,
  2297. OldIrql);
  2298. //
  2299. // Returns with PFN lock held.
  2300. //
  2301. PfnHeld = TRUE;
  2302. }
  2303. else if (TempPte.u.Soft.Transition == 1) {
  2304. //
  2305. // Transition.
  2306. //
  2307. ASSERT (OldIrql != MM_NOIRQL);
  2308. status = MiResolveTransitionFault (FaultingAddress,
  2309. PointerProtoPte,
  2310. Process,
  2311. OldIrql,
  2312. ApcNeeded,
  2313. &CapturedEvent);
  2314. //
  2315. // Returns with PFN lock held.
  2316. //
  2317. PfnHeld = TRUE;
  2318. }
  2319. else if (TempPte.u.Soft.PageFileHigh == 0) {
  2320. //
  2321. // Demand Zero.
  2322. //
  2323. ASSERT (OldIrql != MM_NOIRQL);
  2324. status = MiResolveDemandZeroFault (FaultingAddress,
  2325. PointerProtoPte,
  2326. Process,
  2327. OldIrql);
  2328. //
  2329. // Returns with PFN lock held.
  2330. //
  2331. PfnHeld = TRUE;
  2332. }
  2333. else {
  2334. //
  2335. // Paging file.
  2336. //
  2337. status = MiResolvePageFileFault (FaultingAddress,
  2338. PointerProtoPte,
  2339. ReadBlock,
  2340. Process,
  2341. OldIrql);
  2342. //
  2343. // Returns with PFN lock released.
  2344. //
  2345. ASSERT (KeAreAllApcsDisabled () == TRUE);
  2346. PfnHeld = FALSE;
  2347. }
  2348. }
  2349. if (NT_SUCCESS(status)) {
  2350. ASSERT (PointerPte->u.Hard.Valid == 0);
  2351. status = MiCompleteProtoPteFault (StoreInstruction,
  2352. FaultingAddress,
  2353. PointerPte,
  2354. PointerProtoPte,
  2355. OldIrql,
  2356. LockedProtoPfn);
  2357. if (CapturedEvent != NULL) {
  2358. MiFreeInPageSupportBlock (CapturedEvent);
  2359. }
  2360. }
  2361. else {
  2362. if (PfnHeld == TRUE) {
  2363. UNLOCK_PFN (OldIrql);
  2364. }
  2365. ASSERT (KeAreAllApcsDisabled () == TRUE);
  2366. if (CapturedEvent != NULL) {
  2367. MiFreeInPageSupportBlock (CapturedEvent);
  2368. }
  2369. }
  2370. return status;
  2371. }
  2372. NTSTATUS
  2373. MiCompleteProtoPteFault (
  2374. IN ULONG_PTR StoreInstruction,
  2375. IN PVOID FaultingAddress,
  2376. IN PMMPTE PointerPte,
  2377. IN PMMPTE PointerProtoPte,
  2378. IN KIRQL OldIrql,
  2379. IN OUT PMMPFN *LockedProtoPfn
  2380. )
  2381. /*++
  2382. Routine Description:
  2383. This routine completes a prototype PTE fault. It is invoked
  2384. after a read operation has completed bringing the data into
  2385. memory.
  2386. Arguments:
  2387. StoreInstruction - Supplies nonzero if the instruction is trying
  2388. to modify the faulting address (i.e. write
  2389. access required).
  2390. FaultingAddress - Supplies the faulting address.
  2391. PointerPte - Supplies the PTE for the faulting address.
  2392. PointerProtoPte - Supplies a pointer to the prototype PTE to fault in,
  2393. NULL if no prototype PTE exists.
  2394. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  2395. LockedProtoPfn - Supplies a pointer to a non-NULL prototype PTE's PFN
  2396. pointer that was locked down by the caller, or a
  2397. pointer to NULL if the caller did not lock down any
  2398. PFN. This routine may unlock the PFN - if so, it
  2399. must also clear this pointer.
  2400. Return Value:
  2401. NTSTATUS.
  2402. Environment:
  2403. Kernel mode, PFN lock held.
  2404. --*/
  2405. {
  2406. NTSTATUS Status;
  2407. ULONG FreeBit;
  2408. MMPTE TempPte;
  2409. MMPTE ProtoPteContents;
  2410. MMPTE OriginalPte;
  2411. MMWSLE ProtoProtect;
  2412. ULONG MarkPageDirty;
  2413. PFN_NUMBER PageFrameIndex;
  2414. PMMPFN Pfn1;
  2415. PMMPFN Pfn2;
  2416. PMMPTE ContainingPageTablePointer;
  2417. PFILE_OBJECT FileObject;
  2418. LONGLONG FileOffset;
  2419. PSUBSECTION Subsection;
  2420. MMSECTION_FLAGS ControlAreaFlags;
  2421. ULONG Flags;
  2422. WSLE_NUMBER WorkingSetIndex;
  2423. PEPROCESS CurrentProcess;
  2424. MM_PFN_LOCK_ASSERT();
  2425. ASSERT (PointerProtoPte->u.Hard.Valid == 1);
  2426. ProtoPteContents.u.Long = PointerProtoPte->u.Long;
  2427. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&ProtoPteContents);
  2428. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2429. Pfn1->u3.e1.PrototypePte = 1;
  2430. //
  2431. // Capture prefetch fault information.
  2432. //
  2433. OriginalPte = Pfn1->OriginalPte;
  2434. //
  2435. // Prototype PTE is now valid, make the PTE valid.
  2436. //
  2437. // A PTE just went from not present, not transition to
  2438. // present. The share count and valid count must be
  2439. // updated in the page table page which contains this PTE.
  2440. //
  2441. ContainingPageTablePointer = MiGetPteAddress (PointerPte);
  2442. Pfn2 = MI_PFN_ELEMENT (ContainingPageTablePointer->u.Hard.PageFrameNumber);
  2443. Pfn2->u2.ShareCount += 1;
  2444. ProtoProtect.u1.Long = 0;
  2445. if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) {
  2446. //
  2447. // The protection code for the real PTE comes from the real PTE as
  2448. // it was placed there earlier during the handling of this fault.
  2449. //
  2450. ProtoProtect.u1.e1.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(PointerPte);
  2451. }
  2452. else {
  2453. //
  2454. // Use the protection in the prototype PTE to initialize the real PTE.
  2455. //
  2456. ProtoProtect.u1.e1.Protection = MI_GET_PROTECTION_FROM_SOFT_PTE(&OriginalPte);
  2457. ProtoProtect.u1.e1.SameProtectAsProto = 1;
  2458. MI_ASSERT_NOT_SESSION_DATA (PointerPte);
  2459. if ((StoreInstruction != 0) &&
  2460. ((ProtoProtect.u1.e1.Protection & MM_PROTECTION_WRITE_MASK) == 0)) {
  2461. //
  2462. // This is the errant case where the user is trying to write
  2463. // to a readonly subsection in the image. Since we're more than
  2464. // halfway through the fault, take the easy way to clean this up -
  2465. // treat the access as a read for the rest of this trip through
  2466. // the fault. We'll then immediately refault when the instruction
  2467. // is rerun (because it's really a write), and then we'll notice
  2468. // that the user's PTE is not copy-on-write (or even writable!)
  2469. // and return a clean access violation.
  2470. //
  2471. #if DBGXX
  2472. DbgPrint("MM: user tried to write to a readonly subsection in the image! %p %p %p\n",
  2473. FaultingAddress,
  2474. PointerPte,
  2475. PointerProtoPte);
  2476. #endif
  2477. StoreInstruction = 0;
  2478. }
  2479. }
  2480. MI_SNAP_DATA (Pfn1, PointerProtoPte, 6);
  2481. MarkPageDirty = 0;
  2482. //
  2483. // If this is a store instruction and the page is not copy on
  2484. // write, then set the modified bit in the PFN database and
  2485. // the dirty bit in the PTE. The PTE is not set dirty even
  2486. // if the modified bit is set so writes to the page can be
  2487. // tracked for FlushVirtualMemory.
  2488. //
  2489. if ((StoreInstruction != 0) &&
  2490. ((ProtoProtect.u1.e1.Protection & MM_COPY_ON_WRITE_MASK) != MM_COPY_ON_WRITE_MASK)) {
  2491. MarkPageDirty = 1;
  2492. #if DBG
  2493. if (OriginalPte.u.Soft.Prototype == 1) {
  2494. PCONTROL_AREA ControlArea;
  2495. Subsection = MiGetSubsectionAddress (&OriginalPte);
  2496. ControlArea = Subsection->ControlArea;
  2497. if (ControlArea->DereferenceList.Flink != NULL) {
  2498. DbgPrint ("MM: page fault completing to dereferenced CA %p %p %p\n",
  2499. ControlArea, Pfn1, PointerPte);
  2500. DbgBreakPoint ();
  2501. }
  2502. }
  2503. #endif
  2504. MI_SET_MODIFIED (Pfn1, 1, 0xA);
  2505. if ((OriginalPte.u.Soft.Prototype == 0) &&
  2506. (Pfn1->u3.e1.WriteInProgress == 0)) {
  2507. FreeBit = GET_PAGING_FILE_OFFSET (OriginalPte);
  2508. if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
  2509. MiReleaseConfirmedPageFileSpace (OriginalPte);
  2510. }
  2511. Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
  2512. }
  2513. }
  2514. if (*LockedProtoPfn != NULL) {
  2515. //
  2516. // Unlock page containing prototype PTEs.
  2517. //
  2518. // The reference count on the prototype PTE page will
  2519. // always be greater than 1 if it is a genuine prototype
  2520. // PTE pool allocation. However, if it is a fork
  2521. // prototype PTE allocation, it is possible the pool has
  2522. // already been deallocated and in this case, the LockedProtoPfn
  2523. // frame below will be in transition limbo with a share
  2524. // count of 0 and a reference count of 1 awaiting our
  2525. // final dereference below which will put it on the free list.
  2526. //
  2527. ASSERT ((*LockedProtoPfn)->u3.e2.ReferenceCount >= 1);
  2528. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF ((*LockedProtoPfn), 3);
  2529. //
  2530. // Tell our caller we've unlocked it for him.
  2531. //
  2532. *LockedProtoPfn = NULL;
  2533. }
  2534. UNLOCK_PFN (OldIrql);
  2535. MI_MAKE_VALID_PTE (TempPte,
  2536. PageFrameIndex,
  2537. ProtoProtect.u1.e1.Protection,
  2538. PointerPte);
  2539. #if defined (_MIALT4K_)
  2540. TempPte.u.Hard.Cache = ProtoPteContents.u.Hard.Cache;
  2541. #endif
  2542. if (MarkPageDirty != 0) {
  2543. MI_SET_PTE_DIRTY (TempPte);
  2544. }
  2545. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  2546. PERFINFO_SOFTFAULT(Pfn1, FaultingAddress, PERFINFO_LOG_TYPE_PROTOPTEFAULT);
  2547. WorkingSetIndex = MiAddValidPageToWorkingSet (FaultingAddress,
  2548. PointerPte,
  2549. Pfn1,
  2550. (ULONG) ProtoProtect.u1.Long);
  2551. if (WorkingSetIndex == 0) {
  2552. if (ProtoProtect.u1.e1.SameProtectAsProto == 0) {
  2553. //
  2554. // The protection for the prototype PTE is in the WSLE.
  2555. //
  2556. ASSERT (ProtoProtect.u1.e1.Protection != 0);
  2557. TempPte.u.Long = 0;
  2558. TempPte.u.Soft.Protection =
  2559. MI_GET_PROTECTION_FROM_WSLE (&ProtoProtect);
  2560. TempPte.u.Soft.PageFileHigh = MI_PTE_LOOKUP_NEEDED;
  2561. }
  2562. else {
  2563. //
  2564. // The protection is in the prototype PTE.
  2565. //
  2566. TempPte.u.Long = MiProtoAddressForPte (Pfn1->PteAddress);
  2567. }
  2568. TempPte.u.Proto.Prototype = 1;
  2569. //
  2570. // Trim the page since we couldn't add it to the working
  2571. // set list at this time.
  2572. //
  2573. if (FaultingAddress < MmSystemRangeStart) {
  2574. CurrentProcess = PsGetCurrentProcess ();
  2575. }
  2576. else if ((MI_IS_SESSION_ADDRESS (FaultingAddress)) ||
  2577. (MI_IS_SESSION_PTE (FaultingAddress))) {
  2578. CurrentProcess = HYDRA_PROCESS;
  2579. }
  2580. else {
  2581. CurrentProcess = NULL;
  2582. }
  2583. MiTrimPte (FaultingAddress,
  2584. PointerPte,
  2585. Pfn1,
  2586. CurrentProcess,
  2587. TempPte);
  2588. Status = STATUS_NO_MEMORY;
  2589. }
  2590. else {
  2591. Status = STATUS_SUCCESS;
  2592. }
  2593. //
  2594. // Log prefetch fault information now that the PFN lock has been released
  2595. // and the PTE has been made valid. This minimizes PFN lock contention,
  2596. // allows CcPfLogPageFault to allocate (and fault on) pool, and allows other
  2597. // threads in this process to execute without faulting on this address.
  2598. //
  2599. // Note that the process' working set mutex is still held so any other
  2600. // faults or operations on user addresses by other threads in this process
  2601. // will block for the duration of this call.
  2602. //
  2603. if ((CCPF_IS_PREFETCHER_ACTIVE()) && (OriginalPte.u.Soft.Prototype == 1)) {
  2604. Subsection = MiGetSubsectionAddress (&OriginalPte);
  2605. FileObject = Subsection->ControlArea->FilePointer;
  2606. FileOffset = MiStartingOffset (Subsection, PointerProtoPte);
  2607. ControlAreaFlags = Subsection->ControlArea->u.Flags;
  2608. Flags = 0;
  2609. if (ControlAreaFlags.Image) {
  2610. if ((Subsection->StartingSector == 0) &&
  2611. (Subsection->SubsectionBase != Subsection->ControlArea->Segment->PrototypePte)) {
  2612. //
  2613. // This is an image that was built with a linker pre-1995
  2614. // (version 2.39 is one example) that put bss into a separate
  2615. // subsection with zero as a starting file offset field
  2616. // in the on-disk image. The prefetcher will fetch from the
  2617. // wrong offset trying to satisfy these ranges (which are
  2618. // actually demand zero when the fault occurs) so don't let
  2619. // the prefetcher know about ANY access within this subsection.
  2620. //
  2621. goto Finish;
  2622. }
  2623. Flags |= CCPF_TYPE_IMAGE;
  2624. }
  2625. if (ControlAreaFlags.Rom) {
  2626. Flags |= CCPF_TYPE_ROM;
  2627. }
  2628. CcPfLogPageFault (FileObject, FileOffset, Flags);
  2629. }
  2630. Finish:
  2631. ASSERT (PointerPte == MiGetPteAddress(FaultingAddress));
  2632. return Status;
  2633. }
  2634. NTSTATUS
  2635. MiResolveMappedFileFault (
  2636. IN PVOID FaultingAddress,
  2637. IN PMMPTE PointerPte,
  2638. OUT PMMINPAGE_SUPPORT *ReadBlock,
  2639. IN PEPROCESS Process,
  2640. IN KIRQL OldIrql
  2641. )
  2642. /*++
  2643. Routine Description:
  2644. This routine builds the MDL and other structures to allow a
  2645. read operation on a mapped file for a page fault.
  2646. Arguments:
  2647. FaultingAddress - Supplies the faulting address.
  2648. PointerPte - Supplies the PTE for the faulting address.
  2649. ReadBlock - Supplies a pointer to put the address of the read block which
  2650. needs to be completed before an I/O can be issued.
  2651. Process - Supplies a pointer to the process object. If this
  2652. parameter is NULL, then the fault is for system
  2653. space and the process's working set lock is not held.
  2654. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  2655. Return Value:
  2656. status. A status value of STATUS_ISSUE_PAGING_IO is returned
  2657. if this function completes successfully.
  2658. Environment:
  2659. Kernel mode, PFN lock held.
  2660. --*/
  2661. {
  2662. MMPTE TempPte;
  2663. PFN_NUMBER PageFrameIndex;
  2664. PMMPFN Pfn1;
  2665. PMMPFN Pfn2;
  2666. PSUBSECTION Subsection;
  2667. PCONTROL_AREA ControlArea;
  2668. PMDL Mdl;
  2669. ULONG ReadSize;
  2670. PETHREAD CurrentThread;
  2671. PPFN_NUMBER Page;
  2672. PPFN_NUMBER EndPage;
  2673. PMMPTE BasePte;
  2674. PMMPTE CheckPte;
  2675. LARGE_INTEGER StartingOffset;
  2676. LARGE_INTEGER TempOffset;
  2677. PPFN_NUMBER FirstMdlPage;
  2678. PMMINPAGE_SUPPORT ReadBlockLocal;
  2679. ULONG PageColor;
  2680. ULONG ClusterSize;
  2681. PFN_NUMBER AvailablePages;
  2682. PMMPTE PteFramePointer;
  2683. PFN_NUMBER PteFramePage;
  2684. NTSTATUS Status;
  2685. ClusterSize = 0;
  2686. ASSERT (PointerPte->u.Soft.Prototype == 1);
  2687. // *********************************************
  2688. // Mapped File (subsection format)
  2689. // *********************************************
  2690. if ((MmAvailablePages < MM_HIGH_LIMIT) &&
  2691. (MiEnsureAvailablePageOrWait (Process, FaultingAddress, OldIrql))) {
  2692. //
  2693. // A wait operation was performed which dropped the locks,
  2694. // repeat this fault.
  2695. //
  2696. return STATUS_REFAULT;
  2697. }
  2698. #if DBG
  2699. if (MmDebug & MM_DBG_PTE_UPDATE) {
  2700. MiFormatPte (PointerPte);
  2701. }
  2702. #endif
  2703. //
  2704. // Calculate address of subsection for this prototype PTE.
  2705. //
  2706. Subsection = MiGetSubsectionAddress (PointerPte);
  2707. ControlArea = Subsection->ControlArea;
  2708. if (ControlArea->u.Flags.FailAllIo) {
  2709. return STATUS_IN_PAGE_ERROR;
  2710. }
  2711. if (PointerPte >= &Subsection->SubsectionBase[Subsection->PtesInSubsection]) {
  2712. //
  2713. // Attempt to read past the end of this subsection.
  2714. //
  2715. return STATUS_ACCESS_VIOLATION;
  2716. }
  2717. if (ControlArea->u.Flags.Rom == 1) {
  2718. ASSERT (XIPConfigured == TRUE);
  2719. //
  2720. // Calculate the offset to read into the file.
  2721. // offset = base + ((thispte - basepte) << PAGE_SHIFT)
  2722. //
  2723. StartingOffset.QuadPart = MiStartingOffset (Subsection, PointerPte);
  2724. TempOffset = MiEndingOffset(Subsection);
  2725. ASSERT (StartingOffset.QuadPart < TempOffset.QuadPart);
  2726. //
  2727. // Check to see if the read will go past the end of the file,
  2728. // If so, correct the read size and get a zeroed page instead.
  2729. //
  2730. if ((ControlArea->u.Flags.Image) &&
  2731. (((UINT64)StartingOffset.QuadPart + PAGE_SIZE) > (UINT64)TempOffset.QuadPart)) {
  2732. ReadBlockLocal = MiGetInPageSupportBlock (OldIrql, &Status);
  2733. if (ReadBlockLocal == NULL) {
  2734. ASSERT (!NT_SUCCESS (Status));
  2735. return Status;
  2736. }
  2737. *ReadBlock = ReadBlockLocal;
  2738. CurrentThread = PsGetCurrentThread ();
  2739. //
  2740. // Build an MDL for the request.
  2741. //
  2742. Mdl = &ReadBlockLocal->Mdl;
  2743. FirstMdlPage = &ReadBlockLocal->Page[0];
  2744. Page = FirstMdlPage;
  2745. #if DBG
  2746. RtlFillMemoryUlong (Page,
  2747. (MM_MAXIMUM_READ_CLUSTER_SIZE+1) * sizeof(PFN_NUMBER),
  2748. 0xf1f1f1f1);
  2749. #endif
  2750. ReadSize = PAGE_SIZE;
  2751. BasePte = PointerPte;
  2752. ClusterSize = 1;
  2753. goto UseSingleRamPage;
  2754. }
  2755. PageFrameIndex = (PFN_NUMBER) (StartingOffset.QuadPart >> PAGE_SHIFT);
  2756. PageFrameIndex += ((PLARGE_CONTROL_AREA)ControlArea)->StartingFrame;
  2757. //
  2758. // Increment the PFN reference count in the control area for
  2759. // the subsection (the PFN lock is required to modify this field).
  2760. //
  2761. ControlArea->NumberOfPfnReferences += 1;
  2762. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2763. ASSERT (Pfn1->u3.e1.Rom == 1);
  2764. if (Pfn1->u3.e1.PageLocation != 0) {
  2765. ASSERT (Pfn1->u3.e1.PageLocation == StandbyPageList);
  2766. MiUnlinkPageFromList (Pfn1);
  2767. //
  2768. // Update the PFN database - the reference count must be
  2769. // incremented as the share count is going to go from zero to 1.
  2770. //
  2771. ASSERT (Pfn1->u2.ShareCount == 0);
  2772. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  2773. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  2774. Pfn1->u3.e2.ReferenceCount += 1;
  2775. Pfn1->u2.ShareCount += 1;
  2776. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  2777. Pfn1->u3.e1.CacheAttribute = MiCached;
  2778. ASSERT (Pfn1->PteAddress == PointerPte);
  2779. ASSERT (Pfn1->u1.Event == NULL);
  2780. //
  2781. // Determine the page frame number of the page table page which
  2782. // contains this PTE.
  2783. //
  2784. PteFramePointer = MiGetPteAddress (PointerPte);
  2785. if (PteFramePointer->u.Hard.Valid == 0) {
  2786. #if (_MI_PAGING_LEVELS < 3)
  2787. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  2788. #endif
  2789. KeBugCheckEx (MEMORY_MANAGEMENT,
  2790. 0x61940,
  2791. (ULONG_PTR)PointerPte,
  2792. (ULONG_PTR)PteFramePointer->u.Long,
  2793. 0);
  2794. #if (_MI_PAGING_LEVELS < 3)
  2795. }
  2796. #endif
  2797. }
  2798. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  2799. ASSERT (Pfn1->u4.PteFrame == PteFramePage);
  2800. //
  2801. // Increment the share count for the page table page containing
  2802. // this PTE as the PTE is going to be made valid.
  2803. //
  2804. ASSERT (PteFramePage != 0);
  2805. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  2806. Pfn2->u2.ShareCount += 1;
  2807. }
  2808. else {
  2809. ASSERT (Pfn1->u4.InPageError == 0);
  2810. ASSERT (Pfn1->u3.e1.PrototypePte == 1);
  2811. ASSERT (Pfn1->u1.Event == NULL);
  2812. MiInitializePfn (PageFrameIndex, PointerPte, 0);
  2813. }
  2814. //
  2815. // Put the prototype PTE into the valid state.
  2816. //
  2817. MI_MAKE_VALID_PTE (TempPte,
  2818. PageFrameIndex,
  2819. PointerPte->u.Soft.Protection,
  2820. PointerPte);
  2821. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  2822. return STATUS_PAGE_FAULT_TRANSITION;
  2823. }
  2824. CurrentThread = PsGetCurrentThread ();
  2825. ReadBlockLocal = MiGetInPageSupportBlock (OldIrql, &Status);
  2826. if (ReadBlockLocal == NULL) {
  2827. ASSERT (!NT_SUCCESS (Status));
  2828. return Status;
  2829. }
  2830. *ReadBlock = ReadBlockLocal;
  2831. //
  2832. // Build an MDL for the request.
  2833. //
  2834. Mdl = &ReadBlockLocal->Mdl;
  2835. FirstMdlPage = &ReadBlockLocal->Page[0];
  2836. Page = FirstMdlPage;
  2837. #if DBG
  2838. RtlFillMemoryUlong (Page, (MM_MAXIMUM_READ_CLUSTER_SIZE+1) * sizeof(PFN_NUMBER), 0xf1f1f1f1);
  2839. #endif //DBG
  2840. ReadSize = PAGE_SIZE;
  2841. BasePte = PointerPte;
  2842. //
  2843. // Should we attempt to perform page fault clustering?
  2844. //
  2845. AvailablePages = MmAvailablePages;
  2846. if (MiInPageSinglePages != 0) {
  2847. AvailablePages = 0;
  2848. MiInPageSinglePages -= 1;
  2849. }
  2850. if ((!CurrentThread->DisablePageFaultClustering) &&
  2851. (PERFINFO_DO_PAGEFAULT_CLUSTERING()) &&
  2852. (ControlArea->u.Flags.NoModifiedWriting == 0)) {
  2853. if ((AvailablePages > (MmFreeGoal * 2))
  2854. ||
  2855. (((ControlArea->u.Flags.Image != 0) ||
  2856. (CurrentThread->ForwardClusterOnly)) &&
  2857. (AvailablePages > MM_HIGH_LIMIT))) {
  2858. //
  2859. // Cluster up to n pages. This one + n-1.
  2860. //
  2861. ASSERT (MM_HIGH_LIMIT > MM_MAXIMUM_READ_CLUSTER_SIZE + 16);
  2862. ASSERT (AvailablePages > MM_MAXIMUM_READ_CLUSTER_SIZE + 16);
  2863. if (ControlArea->u.Flags.Image == 0) {
  2864. ASSERT (CurrentThread->ReadClusterSize <=
  2865. MM_MAXIMUM_READ_CLUSTER_SIZE);
  2866. ClusterSize = CurrentThread->ReadClusterSize;
  2867. }
  2868. else {
  2869. ClusterSize = MmDataClusterSize;
  2870. if (Subsection->u.SubsectionFlags.Protection &
  2871. MM_PROTECTION_EXECUTE_MASK ) {
  2872. ClusterSize = MmCodeClusterSize;
  2873. }
  2874. }
  2875. EndPage = Page + ClusterSize;
  2876. CheckPte = PointerPte + 1;
  2877. //
  2878. // Try to cluster within the page of PTEs.
  2879. //
  2880. while ((MiIsPteOnPdeBoundary(CheckPte) == 0) &&
  2881. (Page < EndPage) &&
  2882. (CheckPte <
  2883. &Subsection->SubsectionBase[Subsection->PtesInSubsection])
  2884. && (CheckPte->u.Long == BasePte->u.Long)) {
  2885. ControlArea->NumberOfPfnReferences += 1;
  2886. ReadSize += PAGE_SIZE;
  2887. Page += 1;
  2888. CheckPte += 1;
  2889. }
  2890. if ((Page < EndPage) && (!CurrentThread->ForwardClusterOnly)) {
  2891. //
  2892. // Attempt to cluster going backwards from the PTE.
  2893. //
  2894. CheckPte = PointerPte - 1;
  2895. while ((((ULONG_PTR)CheckPte & (PAGE_SIZE - 1)) !=
  2896. (PAGE_SIZE - sizeof(MMPTE))) &&
  2897. (Page < EndPage) &&
  2898. (CheckPte >= Subsection->SubsectionBase) &&
  2899. (CheckPte->u.Long == BasePte->u.Long)) {
  2900. ControlArea->NumberOfPfnReferences += 1;
  2901. ReadSize += PAGE_SIZE;
  2902. Page += 1;
  2903. CheckPte -= 1;
  2904. }
  2905. BasePte = CheckPte + 1;
  2906. }
  2907. }
  2908. }
  2909. //
  2910. //
  2911. // Calculate the offset to read into the file.
  2912. // offset = base + ((thispte - basepte) << PAGE_SHIFT)
  2913. //
  2914. StartingOffset.QuadPart = MiStartingOffset (Subsection, BasePte);
  2915. TempOffset = MiEndingOffset (Subsection);
  2916. ASSERT (StartingOffset.QuadPart < TempOffset.QuadPart);
  2917. UseSingleRamPage:
  2918. //
  2919. // Remove pages to fill in the MDL. This is done here as the
  2920. // base PTE has been determined and can be used for virtual
  2921. // aliasing checks.
  2922. //
  2923. EndPage = FirstMdlPage;
  2924. CheckPte = BasePte;
  2925. while (EndPage < Page) {
  2926. if (Process == HYDRA_PROCESS) {
  2927. PageColor = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
  2928. }
  2929. else if (Process == NULL) {
  2930. PageColor = MI_GET_PAGE_COLOR_FROM_PTE (CheckPte);
  2931. }
  2932. else {
  2933. PageColor = MI_PAGE_COLOR_PTE_PROCESS (CheckPte,
  2934. &Process->NextPageColor);
  2935. }
  2936. *EndPage = MiRemoveAnyPage (PageColor);
  2937. EndPage += 1;
  2938. CheckPte += 1;
  2939. }
  2940. if (Process == HYDRA_PROCESS) {
  2941. PageColor = MI_GET_PAGE_COLOR_FROM_SESSION (MmSessionSpace);
  2942. }
  2943. else if (Process == NULL) {
  2944. PageColor = MI_GET_PAGE_COLOR_FROM_PTE (CheckPte);
  2945. }
  2946. else {
  2947. PageColor = MI_PAGE_COLOR_PTE_PROCESS (CheckPte,
  2948. &Process->NextPageColor);
  2949. }
  2950. //
  2951. // Check to see if the read will go past the end of the file,
  2952. // If so, correct the read size and get a zeroed page.
  2953. //
  2954. InterlockedIncrement ((PLONG) &MmInfoCounters.PageReadIoCount);
  2955. InterlockedExchangeAdd ((PLONG) &MmInfoCounters.PageReadCount,
  2956. (LONG) (ReadSize >> PAGE_SHIFT));
  2957. if ((ControlArea->u.Flags.Image) &&
  2958. (((UINT64)StartingOffset.QuadPart + ReadSize) > (UINT64)TempOffset.QuadPart)) {
  2959. ASSERT ((ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart)
  2960. > (ReadSize - PAGE_SIZE));
  2961. ReadSize = (ULONG)(TempOffset.QuadPart - StartingOffset.QuadPart);
  2962. //
  2963. // Round the offset to a 512-byte offset as this will help filesystems
  2964. // optimize the transfer. Note that filesystems will always zero fill
  2965. // the remainder between VDL and the next 512-byte multiple and we have
  2966. // already zeroed the whole page.
  2967. //
  2968. ReadSize = ((ReadSize + MMSECTOR_MASK) & ~MMSECTOR_MASK);
  2969. PageFrameIndex = MiRemoveZeroPage (PageColor);
  2970. }
  2971. else {
  2972. //
  2973. // We are reading a complete page, no need to get a zeroed page.
  2974. //
  2975. PageFrameIndex = MiRemoveAnyPage (PageColor);
  2976. }
  2977. //
  2978. // Increment the PFN reference count in the control area for
  2979. // the subsection (the PFN lock is required to modify this field).
  2980. //
  2981. ControlArea->NumberOfPfnReferences += 1;
  2982. *Page = PageFrameIndex;
  2983. PageFrameIndex = *(FirstMdlPage + (PointerPte - BasePte));
  2984. //
  2985. // Get a page and put the PTE into the transition state with the
  2986. // read-in-progress flag set.
  2987. //
  2988. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2989. //
  2990. // Initialize MDL for request.
  2991. //
  2992. MmInitializeMdl (Mdl,
  2993. MiGetVirtualAddressMappedByPte (BasePte),
  2994. ReadSize);
  2995. Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
  2996. #if DBG
  2997. if (ReadSize > ((ClusterSize + 1) << PAGE_SHIFT)) {
  2998. KeBugCheckEx (MEMORY_MANAGEMENT, 0x777,(ULONG_PTR)Mdl, (ULONG_PTR)Subsection,
  2999. (ULONG)TempOffset.LowPart);
  3000. }
  3001. #endif //DBG
  3002. MiInitializeReadInProgressPfn (Mdl,
  3003. BasePte,
  3004. &ReadBlockLocal->Event,
  3005. MI_PROTOTYPE_WSINDEX);
  3006. MI_ZERO_USED_PAGETABLE_ENTRIES_IN_INPAGE_SUPPORT(ReadBlockLocal);
  3007. ReadBlockLocal->ReadOffset = StartingOffset;
  3008. ReadBlockLocal->FilePointer = ControlArea->FilePointer;
  3009. ReadBlockLocal->BasePte = BasePte;
  3010. ReadBlockLocal->Pfn = Pfn1;
  3011. return STATUS_ISSUE_PAGING_IO;
  3012. }
  3013. NTSTATUS
  3014. MiWaitForInPageComplete (
  3015. IN PMMPFN Pfn2,
  3016. IN PMMPTE PointerPte,
  3017. IN PVOID FaultingAddress,
  3018. IN PMMPTE PointerPteContents,
  3019. IN PMMINPAGE_SUPPORT InPageSupport,
  3020. IN PEPROCESS CurrentProcess
  3021. )
  3022. /*++
  3023. Routine Description:
  3024. Waits for a page read to complete.
  3025. Arguments:
  3026. Pfn - Supplies a pointer to the PFN element for the page being read.
  3027. PointerPte - Supplies a pointer to the PTE that is in the transition
  3028. state. This can be a prototype PTE address.
  3029. FaultingAddress - Supplies the faulting address.
  3030. PointerPteContents - Supplies the contents of the PTE before the
  3031. working set lock was released.
  3032. InPageSupport - Supplies a pointer to the inpage support structure
  3033. for this read operation.
  3034. Return Value:
  3035. Returns the status of the inpage operation.
  3036. Note that the working set mutex and PFN lock are held upon return !!!
  3037. Environment:
  3038. Kernel mode, APCs disabled. Neither the working set lock nor
  3039. the PFN lock may be held.
  3040. --*/
  3041. {
  3042. PMMVAD ProtoVad;
  3043. PMMPTE NewPointerPte;
  3044. PMMPTE ProtoPte;
  3045. PMMPFN Pfn1;
  3046. PMMPFN Pfn;
  3047. PULONG Va;
  3048. PPFN_NUMBER Page;
  3049. PPFN_NUMBER LastPage;
  3050. ULONG Offset;
  3051. ULONG Protection;
  3052. PMDL Mdl;
  3053. KIRQL OldIrql;
  3054. NTSTATUS status;
  3055. NTSTATUS status2;
  3056. PEPROCESS Process;
  3057. //
  3058. // Wait for the I/O to complete. Note that we can't wait for all
  3059. // the objects simultaneously as other threads/processes could be
  3060. // waiting for the same event. The first thread which completes
  3061. // the wait and gets the PFN lock may reuse the event for another
  3062. // fault before this thread completes its wait.
  3063. //
  3064. KeWaitForSingleObject( &InPageSupport->Event,
  3065. WrPageIn,
  3066. KernelMode,
  3067. FALSE,
  3068. NULL);
  3069. if (CurrentProcess == HYDRA_PROCESS) {
  3070. LOCK_WORKING_SET (&MmSessionSpace->GlobalVirtualAddress->Vm);
  3071. }
  3072. else if (CurrentProcess == PREFETCH_PROCESS) {
  3073. NOTHING;
  3074. }
  3075. else if (CurrentProcess != NULL) {
  3076. LOCK_WS (CurrentProcess);
  3077. }
  3078. else {
  3079. LOCK_SYSTEM_WS (PsGetCurrentThread ());
  3080. }
  3081. LOCK_PFN (OldIrql);
  3082. ASSERT (Pfn2->u3.e2.ReferenceCount != 0);
  3083. //
  3084. // Check to see if this is the first thread to complete the in-page
  3085. // operation.
  3086. //
  3087. Pfn = InPageSupport->Pfn;
  3088. if (Pfn2 != Pfn) {
  3089. ASSERT (Pfn2->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  3090. Pfn2->u3.e1.ReadInProgress = 0;
  3091. }
  3092. //
  3093. // Another thread has already serviced the read, check the
  3094. // io-error flag in the PFN database to ensure the in-page
  3095. // was successful.
  3096. //
  3097. if (Pfn2->u4.InPageError == 1) {
  3098. ASSERT (!NT_SUCCESS(Pfn2->u1.ReadStatus));
  3099. if (MmIsRetryIoStatus(Pfn2->u1.ReadStatus)) {
  3100. return STATUS_REFAULT;
  3101. }
  3102. return Pfn2->u1.ReadStatus;
  3103. }
  3104. if (InPageSupport->u1.e1.Completed == 0) {
  3105. //
  3106. // The ReadInProgress bit for the dummy page is constantly cleared
  3107. // below as there are generally multiple inpage blocks pointing to
  3108. // the same dummy page.
  3109. //
  3110. ASSERT ((Pfn->u3.e1.ReadInProgress == 1) ||
  3111. (Pfn->PteAddress == MI_PF_DUMMY_PAGE_PTE));
  3112. InPageSupport->u1.e1.Completed = 1;
  3113. Mdl = &InPageSupport->Mdl;
  3114. if (InPageSupport->u1.e1.PrefetchMdlHighBits != 0) {
  3115. //
  3116. // This is a prefetcher-issued read.
  3117. //
  3118. Mdl = MI_EXTRACT_PREFETCH_MDL (InPageSupport);
  3119. }
  3120. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  3121. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  3122. }
  3123. ASSERT (Pfn->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  3124. Pfn->u3.e1.ReadInProgress = 0;
  3125. Pfn->u1.Event = NULL;
  3126. #if defined (_WIN64)
  3127. //
  3128. // Page directory and page table pages are never clustered,
  3129. // ensure this is never violated as only one UsedPageTableEntries
  3130. // is kept in the inpage support block.
  3131. //
  3132. if (InPageSupport->UsedPageTableEntries) {
  3133. Page = (PPFN_NUMBER)(Mdl + 1);
  3134. LastPage = Page + ((Mdl->ByteCount - 1) >> PAGE_SHIFT);
  3135. ASSERT (Page == LastPage);
  3136. }
  3137. #if DBGXX
  3138. MiCheckPageTableInPage (Pfn, InPageSupport);
  3139. #endif
  3140. #endif
  3141. MI_INSERT_USED_PAGETABLE_ENTRIES_IN_PFN(Pfn, InPageSupport);
  3142. //
  3143. // Check the IO_STATUS_BLOCK to ensure the in-page completed successfully.
  3144. //
  3145. if (!NT_SUCCESS(InPageSupport->IoStatus.Status)) {
  3146. if (InPageSupport->IoStatus.Status == STATUS_END_OF_FILE) {
  3147. //
  3148. // An attempt was made to read past the end of file
  3149. // zero all the remaining bytes in the read.
  3150. //
  3151. Page = (PPFN_NUMBER)(Mdl + 1);
  3152. LastPage = Page + ((Mdl->ByteCount - 1) >> PAGE_SHIFT);
  3153. while (Page <= LastPage) {
  3154. MiZeroPhysicalPage (*Page, 0);
  3155. MI_ZERO_USED_PAGETABLE_ENTRIES_IN_PFN(MI_PFN_ELEMENT(*Page));
  3156. Page += 1;
  3157. }
  3158. }
  3159. else {
  3160. //
  3161. // In page io error occurred.
  3162. //
  3163. status = InPageSupport->IoStatus.Status;
  3164. status2 = InPageSupport->IoStatus.Status;
  3165. if (status != STATUS_VERIFY_REQUIRED) {
  3166. LOGICAL Retry;
  3167. Retry = FALSE;
  3168. #if DBG
  3169. DbgPrint ("MM: inpage I/O error %X\n",
  3170. InPageSupport->IoStatus.Status);
  3171. #endif
  3172. //
  3173. // If this page is for paged pool or for paged
  3174. // kernel code or page table pages, bugcheck.
  3175. //
  3176. if ((FaultingAddress > MM_HIGHEST_USER_ADDRESS) &&
  3177. (!MI_IS_SYSTEM_CACHE_ADDRESS(FaultingAddress))) {
  3178. if (MmIsRetryIoStatus(status)) {
  3179. if (MiInPageSinglePages == 0) {
  3180. MiInPageSinglePages = 30;
  3181. }
  3182. MiFaultRetries -= 1;
  3183. if (MiFaultRetries & MiFaultRetryMask) {
  3184. Retry = TRUE;
  3185. }
  3186. }
  3187. if (Retry == FALSE) {
  3188. ULONG_PTR PteContents;
  3189. //
  3190. // The prototype PTE resides in paged pool which may
  3191. // not be resident at this point. Check first.
  3192. //
  3193. if (MiIsAddressValid (PointerPte, FALSE) == TRUE) {
  3194. PteContents = *(PULONG_PTR)PointerPte;
  3195. }
  3196. else {
  3197. PteContents = (ULONG_PTR)-1;
  3198. }
  3199. KeBugCheckEx (KERNEL_DATA_INPAGE_ERROR,
  3200. (ULONG_PTR)PointerPte,
  3201. status,
  3202. (ULONG_PTR)FaultingAddress,
  3203. PteContents);
  3204. }
  3205. status2 = STATUS_REFAULT;
  3206. }
  3207. else {
  3208. if (MmIsRetryIoStatus(status)) {
  3209. if (MiInPageSinglePages == 0) {
  3210. MiInPageSinglePages = 30;
  3211. }
  3212. MiUserFaultRetries -= 1;
  3213. if (MiUserFaultRetries & MiUserFaultRetryMask) {
  3214. Retry = TRUE;
  3215. }
  3216. }
  3217. if (Retry == TRUE) {
  3218. status2 = STATUS_REFAULT;
  3219. }
  3220. }
  3221. }
  3222. Page = (PPFN_NUMBER)(Mdl + 1);
  3223. LastPage = Page + ((Mdl->ByteCount - 1) >> PAGE_SHIFT);
  3224. #if DBG
  3225. Process = PsGetCurrentProcess ();
  3226. #endif
  3227. while (Page <= LastPage) {
  3228. Pfn1 = MI_PFN_ELEMENT (*Page);
  3229. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  3230. Pfn1->u4.InPageError = 1;
  3231. Pfn1->u1.ReadStatus = status;
  3232. #if DBG
  3233. Va = (PULONG)MiMapPageInHyperSpaceAtDpc (Process, *Page);
  3234. RtlFillMemoryUlong (Va, PAGE_SIZE, 0x50444142);
  3235. MiUnmapPageInHyperSpaceFromDpc (Process, Va);
  3236. #endif
  3237. Page += 1;
  3238. }
  3239. return status2;
  3240. }
  3241. }
  3242. else {
  3243. MiFaultRetries = 0;
  3244. MiUserFaultRetries = 0;
  3245. if (InPageSupport->IoStatus.Information != Mdl->ByteCount) {
  3246. ASSERT (InPageSupport->IoStatus.Information != 0);
  3247. //
  3248. // Less than a full page was read - zero the remainder
  3249. // of the page.
  3250. //
  3251. Page = (PPFN_NUMBER)(Mdl + 1);
  3252. LastPage = Page + ((Mdl->ByteCount - 1) >> PAGE_SHIFT);
  3253. Page += ((InPageSupport->IoStatus.Information - 1) >> PAGE_SHIFT);
  3254. Offset = BYTE_OFFSET (InPageSupport->IoStatus.Information);
  3255. if (Offset != 0) {
  3256. Process = PsGetCurrentProcess ();
  3257. Va = (PULONG)((PCHAR)MiMapPageInHyperSpaceAtDpc (Process, *Page)
  3258. + Offset);
  3259. RtlZeroMemory (Va, PAGE_SIZE - Offset);
  3260. MiUnmapPageInHyperSpaceFromDpc (Process, Va);
  3261. }
  3262. //
  3263. // Zero any remaining pages within the MDL.
  3264. //
  3265. Page += 1;
  3266. while (Page <= LastPage) {
  3267. MiZeroPhysicalPage (*Page, 0);
  3268. Page += 1;
  3269. }
  3270. }
  3271. //
  3272. // If any filesystem return non-zeroed data for any slop
  3273. // after the VDL but before the next 512-byte offset then this
  3274. // non-zeroed data will overwrite our zeroed page. This would
  3275. // need to be checked for and cleaned up here. Note that the only
  3276. // reason Mm even rounds the MDL request up to a 512-byte offset
  3277. // is so filesystems receive a transfer they can handle optimally,
  3278. // but any transfer size has always worked (although non-512 byte
  3279. // multiples end up getting posted by the filesystem).
  3280. //
  3281. }
  3282. }
  3283. //
  3284. // Prefetcher-issued reads only put prototype PTEs into transition and
  3285. // never fill actual hardware PTEs so these can be returned now.
  3286. //
  3287. if (CurrentProcess == PREFETCH_PROCESS) {
  3288. return STATUS_SUCCESS;
  3289. }
  3290. //
  3291. // Check to see if the faulting PTE has changed.
  3292. //
  3293. NewPointerPte = MiFindActualFaultingPte (FaultingAddress);
  3294. //
  3295. // If this PTE is in prototype PTE format, make the pointer to the
  3296. // PTE point to the prototype PTE.
  3297. //
  3298. if (NewPointerPte == NULL) {
  3299. return STATUS_PTE_CHANGED;
  3300. }
  3301. if (NewPointerPte != PointerPte) {
  3302. //
  3303. // Check to make sure the NewPointerPte is not a prototype PTE
  3304. // which refers to the page being made valid.
  3305. //
  3306. if (NewPointerPte->u.Soft.Prototype == 1) {
  3307. if (NewPointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) {
  3308. ProtoPte = MiCheckVirtualAddress (FaultingAddress,
  3309. &Protection,
  3310. &ProtoVad);
  3311. }
  3312. else {
  3313. ProtoPte = MiPteToProto (NewPointerPte);
  3314. }
  3315. //
  3316. // Make sure the prototype PTE refers to the PTE made valid.
  3317. //
  3318. if (ProtoPte != PointerPte) {
  3319. return STATUS_PTE_CHANGED;
  3320. }
  3321. //
  3322. // If the only difference is the owner mask, everything is okay.
  3323. //
  3324. if (ProtoPte->u.Long != PointerPteContents->u.Long) {
  3325. return STATUS_PTE_CHANGED;
  3326. }
  3327. }
  3328. else {
  3329. return STATUS_PTE_CHANGED;
  3330. }
  3331. }
  3332. else {
  3333. if (NewPointerPte->u.Long != PointerPteContents->u.Long) {
  3334. return STATUS_PTE_CHANGED;
  3335. }
  3336. }
  3337. return STATUS_SUCCESS;
  3338. }
  3339. PMMPTE
  3340. MiFindActualFaultingPte (
  3341. IN PVOID FaultingAddress
  3342. )
  3343. /*++
  3344. Routine Description:
  3345. This routine locates the actual PTE which must be made resident in order
  3346. to complete this fault. Note that for certain cases multiple faults
  3347. are required to make the final page resident.
  3348. Arguments:
  3349. FaultingAddress - Supplies the virtual address which caused the fault.
  3350. Return Value:
  3351. The PTE to be made valid to finish the fault, NULL if the fault should
  3352. be retried.
  3353. Environment:
  3354. Kernel mode, APCs disabled, working set mutex held.
  3355. --*/
  3356. {
  3357. PMMVAD ProtoVad;
  3358. PMMPTE ProtoPteAddress;
  3359. PMMPTE PointerPte;
  3360. PMMPTE PointerFaultingPte;
  3361. ULONG Protection;
  3362. if (MI_IS_PHYSICAL_ADDRESS (FaultingAddress)) {
  3363. return NULL;
  3364. }
  3365. #if (_MI_PAGING_LEVELS >= 4)
  3366. PointerPte = MiGetPxeAddress (FaultingAddress);
  3367. if (PointerPte->u.Hard.Valid == 0) {
  3368. //
  3369. // Page directory parent page is not valid.
  3370. //
  3371. return PointerPte;
  3372. }
  3373. #endif
  3374. #if (_MI_PAGING_LEVELS >= 3)
  3375. PointerPte = MiGetPpeAddress (FaultingAddress);
  3376. if (PointerPte->u.Hard.Valid == 0) {
  3377. //
  3378. // Page directory page is not valid.
  3379. //
  3380. return PointerPte;
  3381. }
  3382. #endif
  3383. PointerPte = MiGetPdeAddress (FaultingAddress);
  3384. if (PointerPte->u.Hard.Valid == 0) {
  3385. //
  3386. // Page table page is not valid.
  3387. //
  3388. return PointerPte;
  3389. }
  3390. PointerPte = MiGetPteAddress (FaultingAddress);
  3391. if (PointerPte->u.Hard.Valid == 1) {
  3392. //
  3393. // Page is already valid, no need to fault it in.
  3394. //
  3395. return NULL;
  3396. }
  3397. if (PointerPte->u.Soft.Prototype == 0) {
  3398. //
  3399. // Page is not a prototype PTE, make this PTE valid.
  3400. //
  3401. return PointerPte;
  3402. }
  3403. //
  3404. // Check to see if the PTE which maps the prototype PTE is valid.
  3405. //
  3406. if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) {
  3407. //
  3408. // Protection is here, PTE must be located in VAD.
  3409. //
  3410. ProtoPteAddress = MiCheckVirtualAddress (FaultingAddress,
  3411. &Protection,
  3412. &ProtoVad);
  3413. if (ProtoPteAddress == NULL) {
  3414. //
  3415. // No prototype PTE means another thread has deleted the VAD while
  3416. // this thread waited for the inpage to complete. Certainly NULL
  3417. // must be returned so a stale PTE is not modified - the instruction
  3418. // will then be reexecuted and an access violation delivered.
  3419. //
  3420. return NULL;
  3421. }
  3422. }
  3423. else {
  3424. //
  3425. // Protection is in ProtoPte.
  3426. //
  3427. ProtoPteAddress = MiPteToProto (PointerPte);
  3428. }
  3429. PointerFaultingPte = MiFindActualFaultingPte (ProtoPteAddress);
  3430. if (PointerFaultingPte == NULL) {
  3431. return PointerPte;
  3432. }
  3433. return PointerFaultingPte;
  3434. }
  3435. PMMPTE
  3436. MiCheckVirtualAddress (
  3437. IN PVOID VirtualAddress,
  3438. OUT PULONG ProtectCode,
  3439. OUT PMMVAD *VadOut
  3440. )
  3441. /*++
  3442. Routine Description:
  3443. This function examines the virtual address descriptors to see
  3444. if the specified virtual address is contained within any of
  3445. the descriptors. If a virtual address descriptor is found
  3446. which contains the specified virtual address, a PTE is built
  3447. from information within the virtual address descriptor and
  3448. returned to the caller.
  3449. Arguments:
  3450. VirtualAddress - Supplies the virtual address to locate within
  3451. a virtual address descriptor.
  3452. ProtectCode - Supplies a pointer to a variable that will receive the
  3453. protection to insert the actual PTE.
  3454. Vad - Supplies a pointer to a variable that will receive the pointer
  3455. to the VAD that was used for validation (or NULL if no VAD was
  3456. used).
  3457. Return Value:
  3458. Returns the PTE which corresponds to the supplied virtual address.
  3459. If no virtual address descriptor is found, a zero PTE is returned.
  3460. Environment:
  3461. Kernel mode, APCs disabled, working set mutex held.
  3462. --*/
  3463. {
  3464. PMMVAD Vad;
  3465. PMMPTE PointerPte;
  3466. PLIST_ENTRY NextEntry;
  3467. PIMAGE_ENTRY_IN_SESSION Image;
  3468. *VadOut = NULL;
  3469. if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS) {
  3470. if (PAGE_ALIGN(VirtualAddress) == (PVOID) MM_SHARED_USER_DATA_VA) {
  3471. //
  3472. // This is the page that is double mapped between
  3473. // user mode and kernel mode. Map it as read only.
  3474. //
  3475. *ProtectCode = MM_READONLY;
  3476. #if defined(_X86PAE_)
  3477. if (MmPaeMask != 0) {
  3478. //
  3479. // For some 32 bit architectures, the fast system call
  3480. // instruction sequence lives in this page hence we must
  3481. // ensure it is executable.
  3482. //
  3483. *ProtectCode = MM_EXECUTE_READ;
  3484. }
  3485. #endif
  3486. return MmSharedUserDataPte;
  3487. }
  3488. Vad = MiLocateAddress (VirtualAddress);
  3489. if (Vad == NULL) {
  3490. *ProtectCode = MM_NOACCESS;
  3491. return NULL;
  3492. }
  3493. //
  3494. // A virtual address descriptor which contains the virtual address
  3495. // has been located. Build the PTE from the information within
  3496. // the virtual address descriptor.
  3497. //
  3498. if (Vad->u.VadFlags.PhysicalMapping == 1) {
  3499. #if defined(_IA64_)
  3500. //
  3501. // This is a banked section for all platforms except IA64. This
  3502. // is because only IA64 (in the MmX86Fault handler for 32-bit apps)
  3503. // calls this routine without first checking for a valid PTE and
  3504. // just returning.
  3505. //
  3506. if (((PMMVAD_LONG)Vad)->u4.Banked == NULL) {
  3507. //
  3508. // This is a physical (non-banked) section which is allowed to
  3509. // take a TB miss, but never a legitimate call to this routine
  3510. // because the corresponding PPE/PDE/PTE must always be valid.
  3511. //
  3512. ASSERT (MiGetPpeAddress(VirtualAddress)->u.Hard.Valid == 1);
  3513. ASSERT (MiGetPdeAddress(VirtualAddress)->u.Hard.Valid == 1);
  3514. PointerPte = MiGetPteAddress(VirtualAddress);
  3515. ASSERT (PointerPte->u.Hard.Valid == 1);
  3516. KeFillEntryTb (VirtualAddress);
  3517. *ProtectCode = MM_NOACCESS;
  3518. return NULL;
  3519. }
  3520. #endif
  3521. //
  3522. // This is definitely a banked section.
  3523. //
  3524. MiHandleBankedSection (VirtualAddress, Vad);
  3525. *ProtectCode = MM_NOACCESS;
  3526. return NULL;
  3527. }
  3528. if (Vad->u.VadFlags.PrivateMemory == 1) {
  3529. //
  3530. // This is a private region of memory. Check to make
  3531. // sure the virtual address has been committed. Note that
  3532. // addresses are dense from the bottom up.
  3533. //
  3534. if (Vad->u.VadFlags.UserPhysicalPages == 1) {
  3535. //
  3536. // These mappings only fault if the access is bad.
  3537. //
  3538. #if 0
  3539. //
  3540. // Note the PTE can only be checked if the PXE, PPE and PDE are
  3541. // all valid, so just comment out the assert for now.
  3542. //
  3543. ASSERT (MiGetPteAddress(VirtualAddress)->u.Long == ZeroPte.u.Long);
  3544. #endif
  3545. *ProtectCode = MM_NOACCESS;
  3546. return NULL;
  3547. }
  3548. if (Vad->u.VadFlags.MemCommit == 1) {
  3549. *ProtectCode = MI_GET_PROTECTION_FROM_VAD(Vad);
  3550. return NULL;
  3551. }
  3552. //
  3553. // The address is reserved but not committed.
  3554. //
  3555. *ProtectCode = MM_NOACCESS;
  3556. return NULL;
  3557. }
  3558. else {
  3559. //
  3560. // This virtual address descriptor refers to a
  3561. // section, calculate the address of the prototype PTE
  3562. // and construct a pointer to the PTE.
  3563. //
  3564. //*******************************************************
  3565. //*******************************************************
  3566. // well here's an interesting problem, how do we know
  3567. // how to set the attributes on the PTE we are creating
  3568. // when we can't look at the prototype PTE without
  3569. // potentially incurring a page fault. In this case
  3570. // PteTemplate would be zero.
  3571. //*******************************************************
  3572. //*******************************************************
  3573. //
  3574. if (Vad->u.VadFlags.ImageMap == 1) {
  3575. //
  3576. // PTE and proto PTEs have the same protection for images.
  3577. //
  3578. *ProtectCode = MM_UNKNOWN_PROTECTION;
  3579. }
  3580. else {
  3581. *ProtectCode = MI_GET_PROTECTION_FROM_VAD(Vad);
  3582. //
  3583. // Opportunistic clustering can use the identical protections
  3584. // so give our caller the green light.
  3585. //
  3586. if (Vad->u2.VadFlags2.ExtendableFile == 0) {
  3587. *VadOut = Vad;
  3588. }
  3589. }
  3590. PointerPte = (PMMPTE)MiGetProtoPteAddress(Vad,
  3591. MI_VA_TO_VPN (VirtualAddress));
  3592. if (PointerPte == NULL) {
  3593. *ProtectCode = MM_NOACCESS;
  3594. }
  3595. if (Vad->u2.VadFlags2.ExtendableFile) {
  3596. //
  3597. // Make sure the data has been committed.
  3598. //
  3599. if ((MI_VA_TO_VPN (VirtualAddress) - Vad->StartingVpn) >
  3600. (ULONG_PTR)((((PMMVAD_LONG)Vad)->u4.ExtendedInfo->CommittedSize - 1)
  3601. >> PAGE_SHIFT)) {
  3602. *ProtectCode = MM_NOACCESS;
  3603. }
  3604. }
  3605. return PointerPte;
  3606. }
  3607. }
  3608. else if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress)) {
  3609. //
  3610. // The virtual address is within the space occupied by PDEs,
  3611. // make the PDE valid.
  3612. //
  3613. if (((PMMPTE)VirtualAddress >= MiGetPteAddress (MM_PAGED_POOL_START)) &&
  3614. ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool)) {
  3615. *ProtectCode = MM_NOACCESS;
  3616. return NULL;
  3617. }
  3618. *ProtectCode = MM_READWRITE;
  3619. return NULL;
  3620. }
  3621. else if (MI_IS_SESSION_ADDRESS (VirtualAddress) == TRUE) {
  3622. //
  3623. // See if the session space address is copy on write.
  3624. //
  3625. MM_SESSION_SPACE_WS_LOCK_ASSERT ();
  3626. PointerPte = NULL;
  3627. *ProtectCode = MM_NOACCESS;
  3628. NextEntry = MmSessionSpace->ImageList.Flink;
  3629. while (NextEntry != &MmSessionSpace->ImageList) {
  3630. Image = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  3631. if ((VirtualAddress >= Image->Address) && (VirtualAddress <= Image->LastAddress)) {
  3632. PointerPte = Image->PrototypePtes +
  3633. (((PCHAR)VirtualAddress - (PCHAR)Image->Address) >> PAGE_SHIFT);
  3634. *ProtectCode = MM_EXECUTE_WRITECOPY;
  3635. break;
  3636. }
  3637. NextEntry = NextEntry->Flink;
  3638. }
  3639. return PointerPte;
  3640. }
  3641. //
  3642. // Address is in system space.
  3643. //
  3644. *ProtectCode = MM_NOACCESS;
  3645. return NULL;
  3646. }
  3647. #if (_MI_PAGING_LEVELS < 3)
  3648. NTSTATUS
  3649. FASTCALL
  3650. MiCheckPdeForPagedPool (
  3651. IN PVOID VirtualAddress
  3652. )
  3653. /*++
  3654. Routine Description:
  3655. This function copies the Page Table Entry for the corresponding
  3656. virtual address from the system process's page directory.
  3657. This allows page table pages to be lazily evaluated for things
  3658. like paged pool and per-session mappings.
  3659. Arguments:
  3660. VirtualAddress - Supplies the virtual address in question.
  3661. Return Value:
  3662. Either success or access violation.
  3663. Environment:
  3664. Kernel mode, DISPATCH level or below.
  3665. --*/
  3666. {
  3667. PMMPTE PointerPde;
  3668. NTSTATUS status;
  3669. if (MI_IS_SESSION_ADDRESS (VirtualAddress) == TRUE) {
  3670. //
  3671. // Virtual address in the session space range.
  3672. //
  3673. return MiCheckPdeForSessionSpace (VirtualAddress);
  3674. }
  3675. if (MI_IS_SESSION_PTE (VirtualAddress) == TRUE) {
  3676. //
  3677. // PTE for the session space range.
  3678. //
  3679. return MiCheckPdeForSessionSpace (VirtualAddress);
  3680. }
  3681. status = STATUS_SUCCESS;
  3682. if (MI_IS_KERNEL_PAGE_TABLE_ADDRESS(VirtualAddress)) {
  3683. //
  3684. // PTE for paged pool.
  3685. //
  3686. PointerPde = MiGetPteAddress (VirtualAddress);
  3687. status = STATUS_WAIT_1;
  3688. }
  3689. else if (VirtualAddress < MmSystemRangeStart) {
  3690. return STATUS_ACCESS_VIOLATION;
  3691. }
  3692. else {
  3693. //
  3694. // Virtual address in paged pool range.
  3695. //
  3696. PointerPde = MiGetPdeAddress (VirtualAddress);
  3697. }
  3698. //
  3699. // Locate the PDE for this page and make it valid.
  3700. //
  3701. if (PointerPde->u.Hard.Valid == 0) {
  3702. //
  3703. // The MI_WRITE_VALID_PTE macro cannot be used here because
  3704. // its ASSERTs could mistakenly fire as multiple processors
  3705. // may execute the instruction below without synchronization.
  3706. //
  3707. InterlockedExchangePte (PointerPde,
  3708. MmSystemPagePtes [((ULONG_PTR)PointerPde &
  3709. (PD_PER_SYSTEM * (sizeof(MMPTE) * PDE_PER_PAGE) - 1)) / sizeof(MMPTE)].u.Long);
  3710. }
  3711. return status;
  3712. }
  3713. NTSTATUS
  3714. FASTCALL
  3715. MiCheckPdeForSessionSpace (
  3716. IN PVOID VirtualAddress
  3717. )
  3718. /*++
  3719. Routine Description:
  3720. This function copies the Page Table Entry for the corresponding
  3721. session virtual address from the current session's data structures.
  3722. This allows page table pages to be lazily evaluated for session mappings.
  3723. The caller must check for the current process having a session space.
  3724. Arguments:
  3725. VirtualAddress - Supplies the virtual address in question.
  3726. Return Value:
  3727. STATUS_WAIT_1 - The mapping has been made valid, retry the fault.
  3728. STATUS_SUCCESS - Did not handle the fault, continue further processing.
  3729. !STATUS_SUCCESS - An access violation has occurred - raise an exception.
  3730. Environment:
  3731. Kernel mode, DISPATCH level or below.
  3732. --*/
  3733. {
  3734. MMPTE TempPde;
  3735. PMMPTE PointerPde;
  3736. PVOID SessionVirtualAddress;
  3737. ULONG Index;
  3738. //
  3739. // First check whether the reference was to a page table page which maps
  3740. // session space. If so, the PDE is retrieved from the session space
  3741. // data structure and made valid.
  3742. //
  3743. if (MI_IS_SESSION_PTE (VirtualAddress) == TRUE) {
  3744. //
  3745. // Verify that the current process has a session space.
  3746. //
  3747. PointerPde = MiGetPdeAddress (MmSessionSpace);
  3748. if (PointerPde->u.Hard.Valid == 0) {
  3749. #if DBG
  3750. DbgPrint("MiCheckPdeForSessionSpace: No current session for PTE %p\n",
  3751. VirtualAddress);
  3752. DbgBreakPoint();
  3753. #endif
  3754. return STATUS_ACCESS_VIOLATION;
  3755. }
  3756. SessionVirtualAddress = MiGetVirtualAddressMappedByPte ((PMMPTE) VirtualAddress);
  3757. PointerPde = MiGetPteAddress (VirtualAddress);
  3758. if (PointerPde->u.Hard.Valid == 1) {
  3759. //
  3760. // The PDE is already valid - another thread must have
  3761. // won the race. Just return.
  3762. //
  3763. return STATUS_WAIT_1;
  3764. }
  3765. //
  3766. // Calculate the session space PDE index and load the
  3767. // PDE from the session space table for this session.
  3768. //
  3769. Index = MiGetPdeSessionIndex (SessionVirtualAddress);
  3770. TempPde.u.Long = MmSessionSpace->PageTables[Index].u.Long;
  3771. if (TempPde.u.Hard.Valid == 1) {
  3772. //
  3773. // The MI_WRITE_VALID_PTE macro cannot be used here because
  3774. // its ASSERTs could mistakenly fire as multiple processors
  3775. // may execute the instruction below without synchronization.
  3776. //
  3777. InterlockedExchangePte (PointerPde, TempPde.u.Long);
  3778. return STATUS_WAIT_1;
  3779. }
  3780. #if DBG
  3781. DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for PTE %p, %p\n",
  3782. PointerPde->u.Long, SessionVirtualAddress);
  3783. DbgBreakPoint();
  3784. #endif
  3785. return STATUS_ACCESS_VIOLATION;
  3786. }
  3787. if (MI_IS_SESSION_ADDRESS (VirtualAddress) == FALSE) {
  3788. //
  3789. // Not a session space fault - tell the caller to try other handlers.
  3790. //
  3791. return STATUS_SUCCESS;
  3792. }
  3793. //
  3794. // Handle PDE faults for references in the session space.
  3795. // Verify that the current process has a session space.
  3796. //
  3797. PointerPde = MiGetPdeAddress (MmSessionSpace);
  3798. if (PointerPde->u.Hard.Valid == 0) {
  3799. #if DBG
  3800. DbgPrint("MiCheckPdeForSessionSpace: No current session for VA %p\n",
  3801. VirtualAddress);
  3802. DbgBreakPoint();
  3803. #endif
  3804. return STATUS_ACCESS_VIOLATION;
  3805. }
  3806. PointerPde = MiGetPdeAddress (VirtualAddress);
  3807. if (PointerPde->u.Hard.Valid == 0) {
  3808. //
  3809. // Calculate the session space PDE index and load the
  3810. // PDE from the session space table for this session.
  3811. //
  3812. Index = MiGetPdeSessionIndex (VirtualAddress);
  3813. PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
  3814. if (PointerPde->u.Hard.Valid == 1) {
  3815. return STATUS_WAIT_1;
  3816. }
  3817. #if DBG
  3818. DbgPrint("MiCheckPdeForSessionSpace: No Session PDE for VA %p, %p\n",
  3819. PointerPde->u.Long, VirtualAddress);
  3820. DbgBreakPoint();
  3821. #endif
  3822. return STATUS_ACCESS_VIOLATION;
  3823. }
  3824. //
  3825. // Tell the caller to continue with other fault handlers.
  3826. //
  3827. return STATUS_SUCCESS;
  3828. }
  3829. #endif
  3830. VOID
  3831. MiInitializePfn (
  3832. IN PFN_NUMBER PageFrameIndex,
  3833. IN PMMPTE PointerPte,
  3834. IN ULONG ModifiedState
  3835. )
  3836. /*++
  3837. Routine Description:
  3838. This function initializes the specified PFN element to the
  3839. active and valid state.
  3840. Arguments:
  3841. PageFrameIndex - Supplies the page frame number to initialize.
  3842. PointerPte - Supplies the pointer to the PTE which caused the
  3843. page fault.
  3844. ModifiedState - Supplies the state to set the modified field in the PFN
  3845. element for this page, either 0 or 1.
  3846. Return Value:
  3847. None.
  3848. Environment:
  3849. Kernel mode, APCs disabled, PFN lock held.
  3850. --*/
  3851. {
  3852. PMMPFN Pfn1;
  3853. PMMPFN Pfn2;
  3854. PMMPTE PteFramePointer;
  3855. PFN_NUMBER PteFramePage;
  3856. MM_PFN_LOCK_ASSERT();
  3857. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  3858. Pfn1->PteAddress = PointerPte;
  3859. //
  3860. // If the PTE is currently valid, an address space is being built,
  3861. // just make the original PTE demand zero.
  3862. //
  3863. if (PointerPte->u.Hard.Valid == 1) {
  3864. Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
  3865. #if defined (_AMD64_)
  3866. if (PointerPte->u.Hard.NoExecute == 0) {
  3867. Pfn1->OriginalPte.u.Soft.Protection = MM_EXECUTE_READWRITE;
  3868. }
  3869. #endif
  3870. #if defined(_X86PAE_)
  3871. if (MmPaeMask != 0) {
  3872. if ((PointerPte->u.Long & MmPaeMask) == 0) {
  3873. Pfn1->OriginalPte.u.Soft.Protection = MM_EXECUTE_READWRITE;
  3874. }
  3875. }
  3876. #endif
  3877. #if defined(_IA64_)
  3878. if (PointerPte->u.Hard.Execute == 1) {
  3879. Pfn1->OriginalPte.u.Soft.Protection = MM_EXECUTE_READWRITE;
  3880. }
  3881. #endif
  3882. #if defined (_MIALT4K_)
  3883. if (PointerPte->u.Hard.Cache == MM_PTE_CACHE_RESERVED) {
  3884. Pfn1->OriginalPte.u.Soft.SplitPermissions = 1;
  3885. }
  3886. #endif
  3887. if (MI_IS_CACHING_DISABLED (PointerPte)) {
  3888. Pfn1->OriginalPte.u.Soft.Protection = MM_READWRITE | MM_NOCACHE;
  3889. }
  3890. }
  3891. else {
  3892. Pfn1->OriginalPte = *PointerPte;
  3893. ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  3894. (Pfn1->OriginalPte.u.Soft.Transition == 1)));
  3895. }
  3896. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  3897. Pfn1->u3.e2.ReferenceCount += 1;
  3898. Pfn1->u2.ShareCount += 1;
  3899. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  3900. Pfn1->u3.e1.CacheAttribute = MiCached;
  3901. if (ModifiedState == 1) {
  3902. MI_SET_MODIFIED (Pfn1, 1, 0xB);
  3903. }
  3904. else {
  3905. MI_SET_MODIFIED (Pfn1, 0, 0x26);
  3906. }
  3907. #if defined (_WIN64)
  3908. Pfn1->UsedPageTableEntries = 0;
  3909. #endif
  3910. //
  3911. // Determine the page frame number of the page table page which
  3912. // contains this PTE.
  3913. //
  3914. PteFramePointer = MiGetPteAddress(PointerPte);
  3915. if (PteFramePointer->u.Hard.Valid == 0) {
  3916. #if (_MI_PAGING_LEVELS < 3)
  3917. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  3918. #endif
  3919. KeBugCheckEx (MEMORY_MANAGEMENT,
  3920. 0x61940,
  3921. (ULONG_PTR)PointerPte,
  3922. (ULONG_PTR)PteFramePointer->u.Long,
  3923. (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte));
  3924. #if (_MI_PAGING_LEVELS < 3)
  3925. }
  3926. #endif
  3927. }
  3928. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  3929. ASSERT (PteFramePage != 0);
  3930. Pfn1->u4.PteFrame = PteFramePage;
  3931. //
  3932. // Increment the share count for the page table page containing
  3933. // this PTE.
  3934. //
  3935. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  3936. Pfn2->u2.ShareCount += 1;
  3937. return;
  3938. }
  3939. VOID
  3940. MiInitializeReadInProgressSinglePfn (
  3941. IN PFN_NUMBER PageFrameIndex,
  3942. IN PMMPTE BasePte,
  3943. IN PKEVENT Event,
  3944. IN WSLE_NUMBER WorkingSetIndex
  3945. )
  3946. /*++
  3947. Routine Description:
  3948. This function initializes the specified PFN element to the
  3949. transition / read-in-progress state for an in-page operation.
  3950. Arguments:
  3951. PageFrameIndex - Supplies the page frame to initialize.
  3952. BasePte - Supplies the pointer to the PTE for the page frame.
  3953. Event - Supplies the event which is to be set when the I/O operation
  3954. completes.
  3955. WorkingSetIndex - Supplies the working set index flag, a value of
  3956. -1 indicates no WSLE is required because
  3957. this is a prototype PTE.
  3958. Return Value:
  3959. None.
  3960. Environment:
  3961. Kernel mode, APCs disabled, PFN lock held.
  3962. --*/
  3963. {
  3964. PMMPFN Pfn1;
  3965. PMMPTE PteFramePointer;
  3966. PFN_NUMBER PteFramePage;
  3967. MMPTE TempPte;
  3968. MM_PFN_LOCK_ASSERT();
  3969. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  3970. Pfn1->u1.Event = Event;
  3971. Pfn1->PteAddress = BasePte;
  3972. Pfn1->OriginalPte = *BasePte;
  3973. if (WorkingSetIndex == MI_PROTOTYPE_WSINDEX) {
  3974. Pfn1->u3.e1.PrototypePte = 1;
  3975. }
  3976. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  3977. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, TRUE, 10);
  3978. Pfn1->u3.e2.ReferenceCount += 1;
  3979. Pfn1->u2.ShareCount = 0;
  3980. Pfn1->u3.e1.ReadInProgress = 1;
  3981. Pfn1->u3.e1.CacheAttribute = MiCached;
  3982. Pfn1->u4.InPageError = 0;
  3983. //
  3984. // Determine the page frame number of the page table page which
  3985. // contains this PTE.
  3986. //
  3987. PteFramePointer = MiGetPteAddress (BasePte);
  3988. if (PteFramePointer->u.Hard.Valid == 0) {
  3989. #if (_MI_PAGING_LEVELS < 3)
  3990. if (!NT_SUCCESS(MiCheckPdeForPagedPool (BasePte))) {
  3991. #endif
  3992. KeBugCheckEx (MEMORY_MANAGEMENT,
  3993. 0x61940,
  3994. (ULONG_PTR)BasePte,
  3995. (ULONG_PTR)PteFramePointer->u.Long,
  3996. (ULONG_PTR)MiGetVirtualAddressMappedByPte(BasePte));
  3997. #if (_MI_PAGING_LEVELS < 3)
  3998. }
  3999. #endif
  4000. }
  4001. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  4002. Pfn1->u4.PteFrame = PteFramePage;
  4003. //
  4004. // Put the PTE into the transition state, no cache flush needed as
  4005. // PTE is still not valid.
  4006. //
  4007. MI_MAKE_TRANSITION_PTE (TempPte,
  4008. PageFrameIndex,
  4009. BasePte->u.Soft.Protection,
  4010. BasePte);
  4011. MI_WRITE_INVALID_PTE (BasePte, TempPte);
  4012. //
  4013. // Increment the share count for the page table page containing
  4014. // this PTE as the PTE just went into the transition state.
  4015. //
  4016. ASSERT (PteFramePage != 0);
  4017. Pfn1 = MI_PFN_ELEMENT (PteFramePage);
  4018. Pfn1->u2.ShareCount += 1;
  4019. return;
  4020. }
  4021. VOID
  4022. MiInitializeReadInProgressPfn (
  4023. IN PMDL Mdl,
  4024. IN PMMPTE BasePte,
  4025. IN PKEVENT Event,
  4026. IN WSLE_NUMBER WorkingSetIndex
  4027. )
  4028. /*++
  4029. Routine Description:
  4030. This function initializes the specified PFN element to the
  4031. transition / read-in-progress state for an in-page operation.
  4032. Arguments:
  4033. Mdl - Supplies a pointer to the MDL.
  4034. BasePte - Supplies the pointer to the PTE which the first page in
  4035. the MDL maps.
  4036. Event - Supplies the event which is to be set when the I/O operation
  4037. completes.
  4038. WorkingSetIndex - Supplies the working set index flag, a value of
  4039. -1 indicates no WSLE is required because
  4040. this is a prototype PTE.
  4041. Return Value:
  4042. None.
  4043. Environment:
  4044. Kernel mode, APCs disabled, PFN lock held.
  4045. --*/
  4046. {
  4047. PMMPFN Pfn1;
  4048. PMMPFN Pfn2;
  4049. PMMPTE PteFramePointer;
  4050. PFN_NUMBER PteFramePage;
  4051. MMPTE TempPte;
  4052. LONG NumberOfBytes;
  4053. PPFN_NUMBER Page;
  4054. MM_PFN_LOCK_ASSERT();
  4055. Page = (PPFN_NUMBER)(Mdl + 1);
  4056. NumberOfBytes = Mdl->ByteCount;
  4057. while (NumberOfBytes > 0) {
  4058. Pfn1 = MI_PFN_ELEMENT (*Page);
  4059. Pfn1->u1.Event = Event;
  4060. Pfn1->PteAddress = BasePte;
  4061. Pfn1->OriginalPte = *BasePte;
  4062. if (WorkingSetIndex == MI_PROTOTYPE_WSINDEX) {
  4063. Pfn1->u3.e1.PrototypePte = 1;
  4064. }
  4065. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  4066. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, TRUE, 10);
  4067. Pfn1->u3.e2.ReferenceCount += 1;
  4068. Pfn1->u2.ShareCount = 0;
  4069. Pfn1->u3.e1.ReadInProgress = 1;
  4070. Pfn1->u3.e1.CacheAttribute = MiCached;
  4071. Pfn1->u4.InPageError = 0;
  4072. //
  4073. // Determine the page frame number of the page table page which
  4074. // contains this PTE.
  4075. //
  4076. PteFramePointer = MiGetPteAddress(BasePte);
  4077. if (PteFramePointer->u.Hard.Valid == 0) {
  4078. #if (_MI_PAGING_LEVELS < 3)
  4079. if (!NT_SUCCESS(MiCheckPdeForPagedPool (BasePte))) {
  4080. #endif
  4081. KeBugCheckEx (MEMORY_MANAGEMENT,
  4082. 0x61940,
  4083. (ULONG_PTR)BasePte,
  4084. (ULONG_PTR)PteFramePointer->u.Long,
  4085. (ULONG_PTR)MiGetVirtualAddressMappedByPte(BasePte));
  4086. #if (_MI_PAGING_LEVELS < 3)
  4087. }
  4088. #endif
  4089. }
  4090. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  4091. Pfn1->u4.PteFrame = PteFramePage;
  4092. //
  4093. // Put the PTE into the transition state, no cache flush needed as
  4094. // PTE is still not valid.
  4095. //
  4096. MI_MAKE_TRANSITION_PTE (TempPte,
  4097. *Page,
  4098. BasePte->u.Soft.Protection,
  4099. BasePte);
  4100. MI_WRITE_INVALID_PTE (BasePte, TempPte);
  4101. //
  4102. // Increment the share count for the page table page containing
  4103. // this PTE as the PTE just went into the transition state.
  4104. //
  4105. ASSERT (PteFramePage != 0);
  4106. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  4107. Pfn2->u2.ShareCount += 1;
  4108. NumberOfBytes -= PAGE_SIZE;
  4109. Page += 1;
  4110. BasePte += 1;
  4111. }
  4112. return;
  4113. }
  4114. VOID
  4115. MiInitializeTransitionPfn (
  4116. IN PFN_NUMBER PageFrameIndex,
  4117. IN PMMPTE PointerPte
  4118. )
  4119. /*++
  4120. Routine Description:
  4121. This function initializes the specified PFN element to the
  4122. transition state. Main use is by MapImageFile to make the
  4123. page which contains the image header transition in the
  4124. prototype PTEs.
  4125. Arguments:
  4126. PageFrameIndex - Supplies the page frame index to be initialized.
  4127. PointerPte - Supplies an invalid, non-transition PTE to initialize.
  4128. Return Value:
  4129. None.
  4130. Environment:
  4131. Kernel mode, APCs disabled, PFN lock held.
  4132. --*/
  4133. {
  4134. PMMPFN Pfn1;
  4135. PMMPFN Pfn2;
  4136. PMMPTE PteFramePointer;
  4137. PFN_NUMBER PteFramePage;
  4138. MMPTE TempPte;
  4139. MM_PFN_LOCK_ASSERT();
  4140. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4141. Pfn1->u1.Event = NULL;
  4142. Pfn1->PteAddress = PointerPte;
  4143. Pfn1->OriginalPte = *PointerPte;
  4144. ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  4145. (Pfn1->OriginalPte.u.Soft.Transition == 1)));
  4146. //
  4147. // Don't change the reference count (it should already be 1).
  4148. //
  4149. Pfn1->u2.ShareCount = 0;
  4150. //
  4151. // No WSLE is required because this is a prototype PTE.
  4152. //
  4153. Pfn1->u3.e1.PrototypePte = 1;
  4154. Pfn1->u3.e1.PageLocation = TransitionPage;
  4155. Pfn1->u3.e1.CacheAttribute = MiCached;
  4156. //
  4157. // Determine the page frame number of the page table page which
  4158. // contains this PTE.
  4159. //
  4160. PteFramePointer = MiGetPteAddress (PointerPte);
  4161. if (PteFramePointer->u.Hard.Valid == 0) {
  4162. #if (_MI_PAGING_LEVELS < 3)
  4163. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  4164. #endif
  4165. KeBugCheckEx (MEMORY_MANAGEMENT,
  4166. 0x61940,
  4167. (ULONG_PTR)PointerPte,
  4168. (ULONG_PTR)PteFramePointer->u.Long,
  4169. (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte));
  4170. #if (_MI_PAGING_LEVELS < 3)
  4171. }
  4172. #endif
  4173. }
  4174. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  4175. Pfn1->u4.PteFrame = PteFramePage;
  4176. //
  4177. // Put the PTE into the transition state, no cache flush needed as
  4178. // PTE is still not valid.
  4179. //
  4180. MI_MAKE_TRANSITION_PTE (TempPte,
  4181. PageFrameIndex,
  4182. PointerPte->u.Soft.Protection,
  4183. PointerPte);
  4184. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  4185. //
  4186. // Increment the share count for the page table page containing
  4187. // this PTE as the PTE just went into the transition state.
  4188. //
  4189. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  4190. ASSERT (PteFramePage != 0);
  4191. Pfn2->u2.ShareCount += 1;
  4192. return;
  4193. }
  4194. VOID
  4195. MiInitializeCopyOnWritePfn (
  4196. IN PFN_NUMBER PageFrameIndex,
  4197. IN PMMPTE PointerPte,
  4198. IN WSLE_NUMBER WorkingSetIndex,
  4199. IN PVOID SessionPointer
  4200. )
  4201. /*++
  4202. Routine Description:
  4203. This function initializes the specified PFN element to the
  4204. active and valid state for a copy on write operation.
  4205. In this case the page table page which contains the PTE has
  4206. the proper ShareCount.
  4207. Arguments:
  4208. PageFrameIndex - Supplies the page frame number to initialize.
  4209. PointerPte - Supplies the pointer to the PTE which caused the
  4210. page fault.
  4211. WorkingSetIndex - Supplies the working set index for the corresponding
  4212. virtual address.
  4213. SessionPointer - Supplies the session space pointer if this fault is for
  4214. a session space page or NULL if this is for a user page.
  4215. Return Value:
  4216. None.
  4217. Environment:
  4218. Kernel mode, APCs disabled, PFN lock held.
  4219. --*/
  4220. {
  4221. PMMPFN Pfn1;
  4222. PMMPTE PteFramePointer;
  4223. PFN_NUMBER PteFramePage;
  4224. PVOID VirtualAddress;
  4225. PMM_SESSION_SPACE SessionSpace;
  4226. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4227. Pfn1->PteAddress = PointerPte;
  4228. //
  4229. // Get the protection for the page.
  4230. //
  4231. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  4232. Pfn1->OriginalPte.u.Long = 0;
  4233. if (SessionPointer) {
  4234. Pfn1->OriginalPte.u.Soft.Protection = MM_EXECUTE_READWRITE;
  4235. SessionSpace = (PMM_SESSION_SPACE) SessionPointer;
  4236. SessionSpace->Wsle[WorkingSetIndex].u1.e1.Protection =
  4237. MM_EXECUTE_READWRITE;
  4238. }
  4239. else {
  4240. Pfn1->OriginalPte.u.Soft.Protection =
  4241. MI_MAKE_PROTECT_NOT_WRITE_COPY (
  4242. MmWsle[WorkingSetIndex].u1.e1.Protection);
  4243. }
  4244. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  4245. Pfn1->u3.e2.ReferenceCount += 1;
  4246. Pfn1->u2.ShareCount += 1;
  4247. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  4248. Pfn1->u3.e1.CacheAttribute = MiCached;
  4249. Pfn1->u1.WsIndex = WorkingSetIndex;
  4250. //
  4251. // Determine the page frame number of the page table page which
  4252. // contains this PTE.
  4253. //
  4254. PteFramePointer = MiGetPteAddress (PointerPte);
  4255. if (PteFramePointer->u.Hard.Valid == 0) {
  4256. #if (_MI_PAGING_LEVELS < 3)
  4257. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  4258. #endif
  4259. KeBugCheckEx (MEMORY_MANAGEMENT,
  4260. 0x61940,
  4261. (ULONG_PTR)PointerPte,
  4262. (ULONG_PTR)PteFramePointer->u.Long,
  4263. (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte));
  4264. #if (_MI_PAGING_LEVELS < 3)
  4265. }
  4266. #endif
  4267. }
  4268. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  4269. ASSERT (PteFramePage != 0);
  4270. Pfn1->u4.PteFrame = PteFramePage;
  4271. //
  4272. // Set the modified flag in the PFN database as we are writing
  4273. // into this page and the dirty bit is already set in the PTE.
  4274. //
  4275. MI_SET_MODIFIED (Pfn1, 1, 0xC);
  4276. return;
  4277. }
  4278. BOOLEAN
  4279. MiIsAddressValid (
  4280. IN PVOID VirtualAddress,
  4281. IN LOGICAL UseForceIfPossible
  4282. )
  4283. /*++
  4284. Routine Description:
  4285. For a given virtual address this function returns TRUE if no page fault
  4286. will occur for a read operation on the address, FALSE otherwise.
  4287. Note that after this routine was called, if appropriate locks are not
  4288. held, a non-faulting address could fault.
  4289. Arguments:
  4290. VirtualAddress - Supplies the virtual address to check.
  4291. UseForceIfPossible - Supplies TRUE if the address should be forced valid
  4292. if possible.
  4293. Return Value:
  4294. TRUE if no page fault would be generated reading the virtual address,
  4295. FALSE otherwise.
  4296. Environment:
  4297. Kernel mode.
  4298. --*/
  4299. {
  4300. PMMPTE PointerPte;
  4301. #if defined(_IA64_)
  4302. ULONG Region;
  4303. Region = (ULONG)(((ULONG_PTR) VirtualAddress & VRN_MASK) >> 61);
  4304. if ((Region == 0) || (Region == 1) || (Region == 4) || (Region == 7)) {
  4305. NOTHING;
  4306. }
  4307. else {
  4308. return FALSE;
  4309. }
  4310. if (MiIsVirtualAddressMappedByTr (VirtualAddress) == TRUE) {
  4311. return TRUE;
  4312. }
  4313. if (MiMappingsInitialized == FALSE) {
  4314. return FALSE;
  4315. }
  4316. #else
  4317. UNREFERENCED_PARAMETER (UseForceIfPossible);
  4318. #endif
  4319. #if defined (_AMD64_)
  4320. //
  4321. // If this is within the physical addressing range, just return TRUE.
  4322. //
  4323. if (MI_IS_PHYSICAL_ADDRESS(VirtualAddress)) {
  4324. PFN_NUMBER PageFrameIndex;
  4325. //
  4326. // Only bound with MmHighestPhysicalPage once Mm has initialized.
  4327. //
  4328. if (MmHighestPhysicalPage != 0) {
  4329. PageFrameIndex = MI_CONVERT_PHYSICAL_TO_PFN(VirtualAddress);
  4330. if (PageFrameIndex > MmHighestPhysicalPage) {
  4331. return FALSE;
  4332. }
  4333. }
  4334. return TRUE;
  4335. }
  4336. #endif
  4337. //
  4338. // If the address is not canonical then return FALSE as the caller (which
  4339. // may be the kernel debugger) is not expecting to get an unimplemented
  4340. // address bit fault.
  4341. //
  4342. if (MI_RESERVED_BITS_CANONICAL(VirtualAddress) == FALSE) {
  4343. return FALSE;
  4344. }
  4345. #if (_MI_PAGING_LEVELS >= 4)
  4346. PointerPte = MiGetPxeAddress (VirtualAddress);
  4347. if (PointerPte->u.Hard.Valid == 0) {
  4348. return FALSE;
  4349. }
  4350. #endif
  4351. #if (_MI_PAGING_LEVELS >= 3)
  4352. PointerPte = MiGetPpeAddress (VirtualAddress);
  4353. if (PointerPte->u.Hard.Valid == 0) {
  4354. return FALSE;
  4355. }
  4356. #endif
  4357. PointerPte = MiGetPdeAddress (VirtualAddress);
  4358. if (PointerPte->u.Hard.Valid == 0) {
  4359. return FALSE;
  4360. }
  4361. if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) {
  4362. return TRUE;
  4363. }
  4364. PointerPte = MiGetPteAddress (VirtualAddress);
  4365. if (PointerPte->u.Hard.Valid == 0) {
  4366. return FALSE;
  4367. }
  4368. //
  4369. // Make sure we're not treating a page directory as a page table here for
  4370. // the case where the page directory is mapping a large page. This is
  4371. // because the large page bit is valid in PDE formats, but reserved in
  4372. // PTE formats and will cause a trap. A virtual address like c0200000 (on
  4373. // x86) triggers this case.
  4374. //
  4375. if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) {
  4376. #if defined (_MIALT4K_)
  4377. if ((VirtualAddress < MmSystemRangeStart) &&
  4378. (PsGetCurrentProcess()->Wow64Process != NULL)) {
  4379. //
  4380. // Alternate PTEs for emulated processes share the same
  4381. // encoding as large pages so for these it's ok to proceed.
  4382. //
  4383. NOTHING;
  4384. }
  4385. else
  4386. #endif
  4387. return FALSE;
  4388. }
  4389. #if defined(_IA64_)
  4390. if (MI_GET_ACCESSED_IN_PTE (PointerPte) == 0) {
  4391. //
  4392. // Even though the address is valid, the access bit is off so a
  4393. // reference would cause a fault so return FALSE. We may want to
  4394. // rethink this later to instead update the PTE accessed bit if the
  4395. // PFN lock and relevant working set mutex are not currently held.
  4396. //
  4397. if (UseForceIfPossible == TRUE) {
  4398. MI_SET_ACCESSED_IN_PTE (PointerPte, 1);
  4399. if (MI_GET_ACCESSED_IN_PTE (PointerPte) == 1) {
  4400. return TRUE;
  4401. }
  4402. }
  4403. return FALSE;
  4404. }
  4405. #endif
  4406. return TRUE;
  4407. }
  4408. BOOLEAN
  4409. MmIsAddressValid (
  4410. IN PVOID VirtualAddress
  4411. )
  4412. /*++
  4413. Routine Description:
  4414. For a given virtual address this function returns TRUE if no page fault
  4415. will occur for a read operation on the address, FALSE otherwise.
  4416. Note that after this routine was called, if appropriate locks are not
  4417. held, a non-faulting address could fault.
  4418. Arguments:
  4419. VirtualAddress - Supplies the virtual address to check.
  4420. Return Value:
  4421. TRUE if no page fault would be generated reading the virtual address,
  4422. FALSE otherwise.
  4423. Environment:
  4424. Kernel mode.
  4425. --*/
  4426. {
  4427. return MiIsAddressValid (VirtualAddress, FALSE);
  4428. }
  4429. VOID
  4430. MiInitializePfnForOtherProcess (
  4431. IN PFN_NUMBER PageFrameIndex,
  4432. IN PMMPTE PointerPte,
  4433. IN PFN_NUMBER ContainingPageFrame
  4434. )
  4435. /*++
  4436. Routine Description:
  4437. This function initializes the specified PFN element to the
  4438. active and valid state with the dirty bit on in the PTE and
  4439. the PFN database marked as modified.
  4440. As this PTE is not visible from the current process, the containing
  4441. page frame must be supplied at the PTE contents field for the
  4442. PFN database element are set to demand zero.
  4443. Arguments:
  4444. PageFrameIndex - Supplies the page frame number of which to initialize.
  4445. PointerPte - Supplies the pointer to the PTE which caused the
  4446. page fault.
  4447. ContainingPageFrame - Supplies the page frame number of the page
  4448. table page which contains this PTE.
  4449. If the ContainingPageFrame is 0, then
  4450. the ShareCount for the
  4451. containing page is not incremented.
  4452. Return Value:
  4453. None.
  4454. Environment:
  4455. Kernel mode, APCs disabled, PFN lock held.
  4456. --*/
  4457. {
  4458. PMMPFN Pfn1;
  4459. PMMPFN Pfn2;
  4460. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4461. Pfn1->PteAddress = PointerPte;
  4462. //
  4463. // Note that pages allocated this way are for the kernel and thus
  4464. // never have split permissions in the PTE or the PFN.
  4465. //
  4466. Pfn1->OriginalPte.u.Long = MM_DEMAND_ZERO_WRITE_PTE;
  4467. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  4468. Pfn1->u3.e2.ReferenceCount += 1;
  4469. #if DBG
  4470. if (Pfn1->u3.e2.ReferenceCount > 1) {
  4471. DbgPrint ("MM:incrementing ref count > 1 \n");
  4472. MiFormatPfn (Pfn1);
  4473. MiFormatPte (PointerPte);
  4474. }
  4475. #endif
  4476. Pfn1->u2.ShareCount += 1;
  4477. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  4478. //
  4479. // Set the page attribute to cached even though it isn't really mapped
  4480. // into a TB entry yet - it will be when the I/O completes and in the
  4481. // future, may get paged in and out multiple times and will be marked
  4482. // as cached in those transactions also. If in fact the driver stack
  4483. // wants to map it some other way for the transfer, the correct mapping
  4484. // will get used regardless.
  4485. //
  4486. Pfn1->u3.e1.CacheAttribute = MiCached;
  4487. MI_SET_MODIFIED (Pfn1, 1, 0xD);
  4488. Pfn1->u4.InPageError = 0;
  4489. //
  4490. // Increment the share count for the page table page containing
  4491. // this PTE.
  4492. //
  4493. if (ContainingPageFrame != 0) {
  4494. Pfn1->u4.PteFrame = ContainingPageFrame;
  4495. Pfn2 = MI_PFN_ELEMENT (ContainingPageFrame);
  4496. Pfn2->u2.ShareCount += 1;
  4497. }
  4498. return;
  4499. }
  4500. WSLE_NUMBER
  4501. MiAddValidPageToWorkingSet (
  4502. IN PVOID VirtualAddress,
  4503. IN PMMPTE PointerPte,
  4504. IN PMMPFN Pfn1,
  4505. IN ULONG WsleMask
  4506. )
  4507. /*++
  4508. Routine Description:
  4509. This routine adds the specified virtual address into the
  4510. appropriate working set list.
  4511. Arguments:
  4512. VirtualAddress - Supplies the address to add to the working set list.
  4513. PointerPte - Supplies a pointer to the PTE that is now valid.
  4514. Pfn1 - Supplies the PFN database element for the physical page
  4515. mapped by the virtual address.
  4516. WsleMask - Supplies a mask (protection and flags) to OR into the
  4517. working set list entry.
  4518. Return Value:
  4519. Non-zero on success, 0 on failure.
  4520. Environment:
  4521. Kernel mode, APCs disabled, working set lock. PFN lock NOT held.
  4522. --*/
  4523. {
  4524. WSLE_NUMBER WorkingSetIndex;
  4525. PEPROCESS Process;
  4526. PMMSUPPORT WsInfo;
  4527. #if !DBG
  4528. UNREFERENCED_PARAMETER (PointerPte);
  4529. #endif
  4530. ASSERT (MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
  4531. ASSERT (PointerPte->u.Hard.Valid == 1);
  4532. if (MI_IS_SESSION_ADDRESS (VirtualAddress) || MI_IS_SESSION_PTE (VirtualAddress)) {
  4533. //
  4534. // Current process's session space working set.
  4535. //
  4536. WsInfo = &MmSessionSpace->Vm;
  4537. }
  4538. else if (MI_IS_PROCESS_SPACE_ADDRESS(VirtualAddress)) {
  4539. //
  4540. // Per process working set.
  4541. //
  4542. Process = PsGetCurrentProcess();
  4543. WsInfo = &Process->Vm;
  4544. PERFINFO_ADDTOWS(Pfn1, VirtualAddress, Process->UniqueProcessId)
  4545. }
  4546. else {
  4547. //
  4548. // System cache working set.
  4549. //
  4550. WsInfo = &MmSystemCacheWs;
  4551. PERFINFO_ADDTOWS(Pfn1, VirtualAddress, (HANDLE) -1);
  4552. }
  4553. WorkingSetIndex = MiAllocateWsle (WsInfo, PointerPte, Pfn1, WsleMask);
  4554. return WorkingSetIndex;
  4555. }
  4556. VOID
  4557. MiTrimPte (
  4558. IN PVOID VirtualAddress,
  4559. IN PMMPTE PointerPte,
  4560. IN PMMPFN Pfn1,
  4561. IN PEPROCESS CurrentProcess,
  4562. IN MMPTE NewPteContents
  4563. )
  4564. /*++
  4565. Routine Description:
  4566. This routine removes the specified virtual address from a page table
  4567. page. Note there is no working set list entry for this address.
  4568. Arguments:
  4569. VirtualAddress - Supplies the address to remove.
  4570. PointerPte - Supplies a pointer to the PTE that is now valid.
  4571. Pfn1 - Supplies the PFN database element for the physical page
  4572. mapped by the virtual address.
  4573. CurrentProcess - Supplies NULL (ie: system cache), HYDRA_PROCESS (ie:
  4574. a session) or anything else (ie: process).
  4575. NewPteContents - Supplies the new PTE contents to place in the PTE. This
  4576. is only used for prototype PTEs - private PTEs are
  4577. always encoded with the Pfn's OriginalPte information.
  4578. Return Value:
  4579. None.
  4580. Environment:
  4581. Kernel mode, APCs disabled, working set lock. PFN lock NOT held.
  4582. --*/
  4583. {
  4584. KIRQL OldIrql;
  4585. MMPTE TempPte;
  4586. MMPTE PreviousPte;
  4587. PMMPTE ContainingPageTablePage;
  4588. PMMPFN Pfn2;
  4589. PFN_NUMBER PageFrameIndex;
  4590. PFN_NUMBER PageTableFrameIndex;
  4591. PETHREAD CurrentThread;
  4592. CurrentThread = PsGetCurrentThread ();
  4593. PageFrameIndex = MI_PFN_ELEMENT_TO_INDEX (Pfn1);
  4594. // Working set mutex must be held.
  4595. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  4596. ASSERT (KeAreAllApcsDisabled () == TRUE);
  4597. if (Pfn1->u3.e1.PrototypePte) {
  4598. ASSERT (MI_IS_PFN_DELETED (Pfn1) == 0);
  4599. //
  4600. // This is a prototype PTE. The PFN database does not contain
  4601. // the contents of this PTE it contains the contents of the
  4602. // prototype PTE. This PTE must be reconstructed to contain
  4603. // a pointer to the prototype PTE.
  4604. //
  4605. // The working set list entry contains information about
  4606. // how to reconstruct the PTE.
  4607. //
  4608. TempPte = NewPteContents;
  4609. ASSERT (NewPteContents.u.Proto.Prototype == 1);
  4610. //
  4611. // Decrement the share count of the containing page table
  4612. // page as the PTE for the removed page is no longer valid
  4613. // or in transition.
  4614. //
  4615. ContainingPageTablePage = MiGetPteAddress (PointerPte);
  4616. #if (_MI_PAGING_LEVELS >= 3)
  4617. ASSERT (ContainingPageTablePage->u.Hard.Valid == 1);
  4618. #else
  4619. if (ContainingPageTablePage->u.Hard.Valid == 0) {
  4620. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  4621. KeBugCheckEx (MEMORY_MANAGEMENT,
  4622. 0x61940,
  4623. (ULONG_PTR) PointerPte,
  4624. (ULONG_PTR) ContainingPageTablePage->u.Long,
  4625. (ULONG_PTR) VirtualAddress);
  4626. }
  4627. }
  4628. #endif
  4629. PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (ContainingPageTablePage);
  4630. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4631. LOCK_PFN (OldIrql);
  4632. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4633. }
  4634. else {
  4635. //
  4636. // This is a private page, make it transition.
  4637. //
  4638. // Assert that the share count is 1 for all user mode pages.
  4639. //
  4640. ASSERT ((Pfn1->u2.ShareCount == 1) ||
  4641. (VirtualAddress > (PVOID)MM_HIGHEST_USER_ADDRESS));
  4642. if (MI_IS_PFN_DELETED (Pfn1)) {
  4643. TempPte = ZeroPte;
  4644. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  4645. LOCK_PFN (OldIrql);
  4646. MiDecrementShareCountInline (Pfn2, Pfn1->u4.PteFrame);
  4647. }
  4648. else {
  4649. TempPte = *PointerPte;
  4650. MI_MAKE_VALID_PTE_TRANSITION (TempPte,
  4651. Pfn1->OriginalPte.u.Soft.Protection);
  4652. LOCK_PFN (OldIrql);
  4653. }
  4654. }
  4655. PreviousPte = *PointerPte;
  4656. ASSERT (PreviousPte.u.Hard.Valid == 1);
  4657. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  4658. if (CurrentProcess == NULL) {
  4659. KeFlushSingleTb (VirtualAddress, TRUE);
  4660. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  4661. }
  4662. else if (CurrentProcess == HYDRA_PROCESS) {
  4663. MI_FLUSH_SINGLE_SESSION_TB (VirtualAddress);
  4664. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  4665. }
  4666. else {
  4667. KeFlushSingleTb (VirtualAddress, FALSE);
  4668. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  4669. if ((Pfn1->u3.e1.PrototypePte == 0) &&
  4670. (MI_IS_PTE_DIRTY(PreviousPte)) &&
  4671. (CurrentProcess->Flags & PS_PROCESS_FLAGS_USING_WRITE_WATCH)) {
  4672. //
  4673. // This process has (or had) write watch VADs.
  4674. // Search now for a write watch region encapsulating
  4675. // the PTE being invalidated.
  4676. //
  4677. MiCaptureWriteWatchDirtyBit (CurrentProcess, VirtualAddress);
  4678. }
  4679. }
  4680. MiDecrementShareCountInline (Pfn1, PageFrameIndex);
  4681. UNLOCK_PFN (OldIrql);
  4682. }
  4683. PMMINPAGE_SUPPORT
  4684. MiGetInPageSupportBlock (
  4685. IN KIRQL OldIrql,
  4686. OUT PNTSTATUS Status
  4687. )
  4688. /*++
  4689. Routine Description:
  4690. This routine acquires an inpage support block. If none are available,
  4691. the PFN lock will be released and reacquired to add an entry to the list.
  4692. NULL will then be returned.
  4693. Arguments:
  4694. OldIrql - Supplies the IRQL the caller acquired the PFN lock at (or
  4695. MM_NOIRQL if the caller did not acquire the PFN at all).
  4696. Status - Supplies a pointer to a status to return (valid only if the
  4697. PFN lock had to be released, ie: NULL was returned).
  4698. Return Value:
  4699. A non-null pointer to an inpage block if one is already available.
  4700. The PFN lock is not released in this path.
  4701. NULL is returned if no inpage blocks were available. In this path, the
  4702. PFN lock is released and an entry is added - but NULL is still returned
  4703. so the caller is aware that the state has changed due to the lock release
  4704. and reacquisition.
  4705. Environment:
  4706. Kernel mode, PFN lock may optionally be held.
  4707. --*/
  4708. {
  4709. PMMINPAGE_SUPPORT Support;
  4710. PSLIST_ENTRY SingleListEntry;
  4711. #if DBG
  4712. if (OldIrql != MM_NOIRQL) {
  4713. MM_PFN_LOCK_ASSERT();
  4714. }
  4715. else {
  4716. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  4717. }
  4718. #endif
  4719. if (ExQueryDepthSList (&MmInPageSupportSListHead) != 0) {
  4720. SingleListEntry = InterlockedPopEntrySList (&MmInPageSupportSListHead);
  4721. if (SingleListEntry != NULL) {
  4722. Support = CONTAINING_RECORD (SingleListEntry,
  4723. MMINPAGE_SUPPORT,
  4724. ListEntry);
  4725. returnok:
  4726. ASSERT (Support->WaitCount == 1);
  4727. ASSERT (Support->u1.e1.PrefetchMdlHighBits == 0);
  4728. ASSERT (Support->u1.LongFlags == 0);
  4729. ASSERT (KeReadStateEvent (&Support->Event) == 0);
  4730. ASSERT64 (Support->UsedPageTableEntries == 0);
  4731. Support->Thread = PsGetCurrentThread();
  4732. #if DBG
  4733. Support->ListEntry.Next = NULL;
  4734. #endif
  4735. return Support;
  4736. }
  4737. }
  4738. if (OldIrql != MM_NOIRQL) {
  4739. UNLOCK_PFN (OldIrql);
  4740. }
  4741. Support = ExAllocatePoolWithTag (NonPagedPool,
  4742. sizeof(MMINPAGE_SUPPORT),
  4743. 'nImM');
  4744. if (Support != NULL) {
  4745. KeInitializeEvent (&Support->Event, NotificationEvent, FALSE);
  4746. Support->WaitCount = 1;
  4747. Support->u1.LongFlags = 0;
  4748. ASSERT (Support->u1.PrefetchMdl == NULL);
  4749. ASSERT (KeReadStateEvent (&Support->Event) == 0);
  4750. #if defined (_WIN64)
  4751. Support->UsedPageTableEntries = 0;
  4752. #endif
  4753. #if DBG
  4754. Support->Thread = NULL;
  4755. #endif
  4756. if (OldIrql == MM_NOIRQL) {
  4757. goto returnok;
  4758. }
  4759. InterlockedPushEntrySList (&MmInPageSupportSListHead,
  4760. (PSLIST_ENTRY)&Support->ListEntry);
  4761. //
  4762. // Pool had to be allocated for this inpage block hence the PFN
  4763. // lock had to be released. No need to delay, but the fault must
  4764. // be retried due to the PFN lock release. Return a status such
  4765. // that this will occur quickly.
  4766. //
  4767. *Status = STATUS_REFAULT;
  4768. }
  4769. else {
  4770. //
  4771. // No pool is available - don't let a high priority thread consume
  4772. // the machine in a continuous refault stream. A delay allows
  4773. // other system threads to run which will try to free up more pool.
  4774. // Return a status that will introduce a delay above us in an effort
  4775. // to make pool available. The advantage of our caller executing the
  4776. // delay is because he'll do this after releasing the relevant
  4777. // working set mutex (if any) so the current process can be trimmed
  4778. // for pages also.
  4779. //
  4780. *Status = STATUS_INSUFFICIENT_RESOURCES;
  4781. }
  4782. if (OldIrql != MM_NOIRQL) {
  4783. LOCK_PFN (OldIrql);
  4784. }
  4785. return NULL;
  4786. }
  4787. VOID
  4788. MiFreeInPageSupportBlock (
  4789. IN PMMINPAGE_SUPPORT Support
  4790. )
  4791. /*++
  4792. Routine Description:
  4793. This routine returns the inpage support block to a list of freed blocks.
  4794. Arguments:
  4795. Support - Supplies the inpage support block to put on the free list.
  4796. Return Value:
  4797. None.
  4798. Environment:
  4799. Kernel mode, APC_LEVEL or below.
  4800. --*/
  4801. {
  4802. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  4803. ASSERT (Support->Thread != NULL);
  4804. ASSERT (Support->WaitCount != 0);
  4805. ASSERT ((Support->ListEntry.Next == NULL) ||
  4806. (Support->u1.e1.PrefetchMdlHighBits != 0));
  4807. //
  4808. // An interlock is needed for the WaitCount decrement as an inpage support
  4809. // block can be simultaneously freed by any number of threads.
  4810. //
  4811. // Careful synchronization is applied to the WaitCount field so
  4812. // that freeing of the inpage block can occur lock-free. Note
  4813. // that the ReadInProgress bit on each PFN is set and cleared while
  4814. // holding the PFN lock. Inpage blocks are always (and must be)
  4815. // freed _AFTER_ the ReadInProgress bit is cleared.
  4816. //
  4817. if (InterlockedDecrement (&Support->WaitCount) == 0) {
  4818. if (Support->u1.e1.PrefetchMdlHighBits != 0) {
  4819. PMDL Mdl;
  4820. Mdl = MI_EXTRACT_PREFETCH_MDL (Support);
  4821. if (Mdl != &Support->Mdl) {
  4822. ExFreePool (Mdl);
  4823. }
  4824. }
  4825. if (ExQueryDepthSList (&MmInPageSupportSListHead) < MmInPageSupportMinimum) {
  4826. Support->WaitCount = 1;
  4827. Support->u1.LongFlags = 0;
  4828. KeClearEvent (&Support->Event);
  4829. #if defined (_WIN64)
  4830. Support->UsedPageTableEntries = 0;
  4831. #endif
  4832. #if DBG
  4833. Support->Thread = NULL;
  4834. #endif
  4835. InterlockedPushEntrySList (&MmInPageSupportSListHead,
  4836. (PSLIST_ENTRY)&Support->ListEntry);
  4837. return;
  4838. }
  4839. ExFreePool (Support);
  4840. }
  4841. return;
  4842. }
  4843. VOID
  4844. MiHandleBankedSection (
  4845. IN PVOID VirtualAddress,
  4846. IN PMMVAD Vad
  4847. )
  4848. /*++
  4849. Routine Description:
  4850. This routine invalidates a bank of video memory, calls out to the
  4851. video driver and then enables the next bank of video memory.
  4852. Arguments:
  4853. VirtualAddress - Supplies the address of the faulting page.
  4854. Vad - Supplies the VAD which maps the range.
  4855. Return Value:
  4856. None.
  4857. Environment:
  4858. Kernel mode, PFN lock held.
  4859. --*/
  4860. {
  4861. PMMBANKED_SECTION Bank;
  4862. PMMPTE PointerPte;
  4863. ULONG BankNumber;
  4864. ULONG size;
  4865. Bank = ((PMMVAD_LONG) Vad)->u4.Banked;
  4866. size = Bank->BankSize;
  4867. RtlFillMemory (Bank->CurrentMappedPte,
  4868. size >> (PAGE_SHIFT - PTE_SHIFT),
  4869. (UCHAR)ZeroPte.u.Long);
  4870. //
  4871. // Flush the TB as we have invalidated all the PTEs in this range.
  4872. //
  4873. KeFlushEntireTb (TRUE, TRUE);
  4874. //
  4875. // Calculate new bank address and bank number.
  4876. //
  4877. PointerPte = MiGetPteAddress (
  4878. (PVOID)((ULONG_PTR)VirtualAddress & ~((LONG)size - 1)));
  4879. Bank->CurrentMappedPte = PointerPte;
  4880. BankNumber = (ULONG)(((PCHAR)PointerPte - (PCHAR)Bank->BasedPte) >> Bank->BankShift);
  4881. (Bank->BankedRoutine) (BankNumber, BankNumber, Bank->Context);
  4882. //
  4883. // Set the new range valid.
  4884. //
  4885. RtlCopyMemory (PointerPte,
  4886. &Bank->BankTemplate[0],
  4887. size >> (PAGE_SHIFT - PTE_SHIFT));
  4888. return;
  4889. }