Source code of Windows XP (NT5)
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.

2448 lines
69 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. mapcache.c
  5. Abstract:
  6. This module contains the routines which implement mapping views
  7. of sections into the system-wide cache.
  8. Author:
  9. Lou Perazzoli (loup) 22-May-1990
  10. Landy Wang (landyw) 02-Jun-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(INIT,MiInitializeSystemCache)
  16. #pragma alloc_text(PAGE,MiAddMappedPtes)
  17. #endif
  18. extern ULONG MmFrontOfList;
  19. #define X256K 0x40000
  20. PMMPTE MmFirstFreeSystemCache;
  21. PMMPTE MmLastFreeSystemCache;
  22. PMMPTE MmSystemCachePteBase;
  23. ULONG MiMapCacheFailures;
  24. LONG
  25. MiMapCacheExceptionFilter (
  26. IN PNTSTATUS Status,
  27. IN PEXCEPTION_POINTERS ExceptionPointer
  28. );
  29. NTSTATUS
  30. MmMapViewInSystemCache (
  31. IN PVOID SectionToMap,
  32. OUT PVOID *CapturedBase,
  33. IN OUT PLARGE_INTEGER SectionOffset,
  34. IN OUT PULONG CapturedViewSize
  35. )
  36. /*++
  37. Routine Description:
  38. This function maps a view in the specified subject process to
  39. the section object. The page protection is identical to that
  40. of the prototype PTE.
  41. This function is a kernel mode interface to allow LPC to map
  42. a section given the section pointer to map.
  43. This routine assumes all arguments have been probed and captured.
  44. Arguments:
  45. SectionToMap - Supplies a pointer to the section object.
  46. BaseAddress - Supplies a pointer to a variable that will receive
  47. the base address of the view. If the initial value
  48. of this argument is not null, then the view will
  49. be allocated starting at the specified virtual
  50. address rounded down to the next 64kb address
  51. boundary. If the initial value of this argument is
  52. null, then the operating system will determine
  53. where to allocate the view using the information
  54. specified by the ZeroBits argument value and the
  55. section allocation attributes (i.e. based and tiled).
  56. SectionOffset - Supplies the offset from the beginning of the section
  57. to the view in bytes. This value must be a multiple
  58. of 256k.
  59. ViewSize - Supplies a pointer to a variable that will receive
  60. the actual size in bytes of the view. The initial
  61. values of this argument specifies the size of the view
  62. in bytes and is rounded up to the next host page size
  63. boundary and must be less than or equal to 256k.
  64. Return Value:
  65. NTSTATUS.
  66. Environment:
  67. Kernel mode, APC_LEVEL or below.
  68. --*/
  69. {
  70. PSECTION Section;
  71. ULONG PteOffset;
  72. ULONG LastPteOffset;
  73. KIRQL OldIrql;
  74. PMMPTE PointerPte;
  75. PMMPTE LastPte;
  76. PMMPTE ProtoPte;
  77. PMMPTE LastProto;
  78. PSUBSECTION Subsection;
  79. PVOID EndingVa;
  80. PCONTROL_AREA ControlArea;
  81. NTSTATUS Status;
  82. LOGICAL FlushNeeded;
  83. ULONG Waited;
  84. #if DBG
  85. PMMPTE PointerPte2;
  86. PMMPTE TimeStampPte;
  87. #endif
  88. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  89. Section = SectionToMap;
  90. Status = STATUS_SUCCESS;
  91. FlushNeeded = FALSE;
  92. //
  93. // Assert the view size is less than 256k and the section offset
  94. // is aligned on a 256k boundary.
  95. //
  96. ASSERT (*CapturedViewSize <= X256K);
  97. ASSERT ((SectionOffset->LowPart & (X256K - 1)) == 0);
  98. //
  99. // Make sure the section is not an image section or a page file
  100. // backed section.
  101. //
  102. if (Section->u.Flags.Image) {
  103. return STATUS_NOT_MAPPED_DATA;
  104. }
  105. ControlArea = Section->Segment->ControlArea;
  106. ASSERT (*CapturedViewSize != 0);
  107. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  108. if (ControlArea->u.Flags.Rom == 0) {
  109. Subsection = (PSUBSECTION)(ControlArea + 1);
  110. }
  111. else {
  112. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  113. }
  114. LOCK_PFN (OldIrql);
  115. ASSERT (ControlArea->u.Flags.BeingCreated == 0);
  116. ASSERT (ControlArea->u.Flags.BeingDeleted == 0);
  117. ASSERT (ControlArea->u.Flags.BeingPurged == 0);
  118. //
  119. // Find a free 256k base in the cache.
  120. //
  121. if (MmFirstFreeSystemCache == (PMMPTE)MM_EMPTY_LIST) {
  122. UNLOCK_PFN (OldIrql);
  123. return STATUS_NO_MEMORY;
  124. }
  125. PointerPte = MmFirstFreeSystemCache;
  126. //
  127. // Update next free entry.
  128. //
  129. ASSERT (PointerPte->u.Hard.Valid == 0);
  130. if (PointerPte->u.List.NextEntry == MM_EMPTY_PTE_LIST) {
  131. KeBugCheckEx (MEMORY_MANAGEMENT,
  132. 0x778,
  133. (ULONG_PTR)PointerPte,
  134. 0,
  135. 0);
  136. }
  137. else {
  138. MmFirstFreeSystemCache = MmSystemCachePteBase + PointerPte->u.List.NextEntry;
  139. ASSERT (MmFirstFreeSystemCache <= MiGetPteAddress (MmSystemCacheEnd));
  140. }
  141. //
  142. // Increment the count of the number of views for the
  143. // section object. This requires the PFN lock to be held.
  144. //
  145. ControlArea->NumberOfMappedViews += 1;
  146. ControlArea->NumberOfSystemCacheViews += 1;
  147. ASSERT (ControlArea->NumberOfSectionReferences != 0);
  148. //
  149. // Check to see if the TB needs to be flushed. Note that due to natural
  150. // TB traffic and the number of system cache views, this is an extremely
  151. // rare operation.
  152. //
  153. if ((PointerPte + 1)->u.List.NextEntry == (KeReadTbFlushTimeStamp() & MM_FLUSH_COUNTER_MASK)) {
  154. FlushNeeded = TRUE;
  155. }
  156. *CapturedBase = MiGetVirtualAddressMappedByPte (PointerPte);
  157. EndingVa = (PVOID)(((ULONG_PTR)*CapturedBase +
  158. *CapturedViewSize - 1L) | (PAGE_SIZE - 1L));
  159. LastPte = MiGetPteAddress (EndingVa);
  160. //
  161. // An unoccupied address range has been found, put the PTEs in
  162. // the range into prototype PTEs.
  163. //
  164. #if DBG
  165. //
  166. // Zero out the next pointer field.
  167. //
  168. PointerPte->u.List.NextEntry = 0;
  169. TimeStampPte = PointerPte + 1;
  170. for (PointerPte2 = PointerPte; PointerPte2 <= LastPte; PointerPte2 += 1) {
  171. ASSERT ((PointerPte2->u.Long == ZeroKernelPte.u.Long) ||
  172. (PointerPte2 == TimeStampPte));
  173. }
  174. #endif
  175. //
  176. // Calculate the first prototype PTE address.
  177. //
  178. PteOffset = (ULONG)(SectionOffset->QuadPart >> PAGE_SHIFT);
  179. LastPteOffset = PteOffset + (ULONG)(LastPte - PointerPte + 1);
  180. //
  181. // Make sure the PTEs are not in the extended part of the
  182. // segment.
  183. //
  184. while (PteOffset >= Subsection->PtesInSubsection) {
  185. PteOffset -= Subsection->PtesInSubsection;
  186. LastPteOffset -= Subsection->PtesInSubsection;
  187. Subsection = Subsection->NextSubsection;
  188. }
  189. //
  190. // Increment the view count for every subsection spanned by this view,
  191. // creating prototype PTEs if needed.
  192. //
  193. // N.B. This call may release and reacquire the PFN lock.
  194. //
  195. // N.B. This call always returns with the PFN lock released !
  196. //
  197. if (ControlArea->FilePointer != NULL) {
  198. Status = MiAddViewsForSection ((PMSUBSECTION)Subsection,
  199. LastPteOffset,
  200. OldIrql,
  201. &Waited);
  202. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  203. }
  204. else {
  205. UNLOCK_PFN (OldIrql);
  206. }
  207. if (FlushNeeded == TRUE) {
  208. KeFlushEntireTb (TRUE, TRUE);
  209. }
  210. if (!NT_SUCCESS (Status)) {
  211. //
  212. // Zero both the next and TB flush stamp PTEs before unmapping so
  213. // the unmap won't hit entries it can't decode.
  214. //
  215. MiMapCacheFailures += 1;
  216. PointerPte->u.List.NextEntry = 0;
  217. (PointerPte+1)->u.List.NextEntry = 0;
  218. MmUnmapViewInSystemCache (*CapturedBase, SectionToMap, FALSE);
  219. return Status;
  220. }
  221. ProtoPte = &Subsection->SubsectionBase[PteOffset];
  222. LastProto = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  223. while (PointerPte <= LastPte) {
  224. if (ProtoPte >= LastProto) {
  225. //
  226. // Handle extended subsections.
  227. //
  228. Subsection = Subsection->NextSubsection;
  229. ProtoPte = Subsection->SubsectionBase;
  230. LastProto = &Subsection->SubsectionBase[
  231. Subsection->PtesInSubsection];
  232. }
  233. PointerPte->u.Long = MiProtoAddressForKernelPte (ProtoPte);
  234. ASSERT (((ULONG_PTR)PointerPte & (MM_COLOR_MASK << PTE_SHIFT)) ==
  235. (((ULONG_PTR)ProtoPte & (MM_COLOR_MASK << PTE_SHIFT))));
  236. PointerPte += 1;
  237. ProtoPte += 1;
  238. }
  239. return STATUS_SUCCESS;
  240. }
  241. NTSTATUS
  242. MiAddMappedPtes (
  243. IN PMMPTE FirstPte,
  244. IN ULONG NumberOfPtes,
  245. IN PCONTROL_AREA ControlArea
  246. )
  247. /*++
  248. Routine Description:
  249. This function maps a view in the current address space to the
  250. specified control area. The page protection is identical to that
  251. of the prototype PTE.
  252. This routine assumes the caller has called MiCheckPurgeAndUpMapCount,
  253. hence the PFN lock is not needed here.
  254. Arguments:
  255. FirstPte - Supplies a pointer to the first PTE of the current address
  256. space to initialize.
  257. NumberOfPtes - Supplies the number of PTEs to initialize.
  258. ControlArea - Supplies the control area to point the PTEs at.
  259. Return Value:
  260. NTSTATUS.
  261. Environment:
  262. Kernel mode.
  263. --*/
  264. {
  265. PMMPTE PointerPte;
  266. PMMPTE ProtoPte;
  267. PMMPTE LastProto;
  268. PMMPTE LastPte;
  269. PSUBSECTION Subsection;
  270. NTSTATUS Status;
  271. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  272. (ControlArea->u.Flags.Rom == 0)) {
  273. Subsection = (PSUBSECTION)(ControlArea + 1);
  274. }
  275. else {
  276. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  277. }
  278. PointerPte = FirstPte;
  279. ASSERT (NumberOfPtes != 0);
  280. LastPte = FirstPte + NumberOfPtes;
  281. ASSERT (ControlArea->NumberOfMappedViews >= 1);
  282. ASSERT (ControlArea->NumberOfUserReferences >= 1);
  283. ASSERT (ControlArea->u.Flags.HadUserReference == 1);
  284. ASSERT (ControlArea->NumberOfSectionReferences != 0);
  285. ASSERT (ControlArea->u.Flags.BeingCreated == 0);
  286. ASSERT (ControlArea->u.Flags.BeingDeleted == 0);
  287. ASSERT (ControlArea->u.Flags.BeingPurged == 0);
  288. if ((ControlArea->FilePointer != NULL) &&
  289. (ControlArea->u.Flags.Image == 0) &&
  290. (ControlArea->u.Flags.PhysicalMemory == 0)) {
  291. //
  292. // Increment the view count for every subsection spanned by this view.
  293. //
  294. Status = MiAddViewsForSectionWithPfn ((PMSUBSECTION)Subsection,
  295. NumberOfPtes);
  296. if (!NT_SUCCESS (Status)) {
  297. return Status;
  298. }
  299. }
  300. ProtoPte = Subsection->SubsectionBase;
  301. LastProto = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  302. while (PointerPte < LastPte) {
  303. if (ProtoPte >= LastProto) {
  304. //
  305. // Handle extended subsections.
  306. //
  307. Subsection = Subsection->NextSubsection;
  308. ProtoPte = Subsection->SubsectionBase;
  309. LastProto = &Subsection->SubsectionBase[
  310. Subsection->PtesInSubsection];
  311. }
  312. ASSERT (PointerPte->u.Long == ZeroKernelPte.u.Long);
  313. PointerPte->u.Long = MiProtoAddressForKernelPte (ProtoPte);
  314. ASSERT (((ULONG_PTR)PointerPte & (MM_COLOR_MASK << PTE_SHIFT)) ==
  315. (((ULONG_PTR)ProtoPte & (MM_COLOR_MASK << PTE_SHIFT))));
  316. PointerPte += 1;
  317. ProtoPte += 1;
  318. }
  319. return STATUS_SUCCESS;
  320. }
  321. VOID
  322. MmUnmapViewInSystemCache (
  323. IN PVOID BaseAddress,
  324. IN PVOID SectionToUnmap,
  325. IN ULONG AddToFront
  326. )
  327. /*++
  328. Routine Description:
  329. This function unmaps a view from the system cache.
  330. NOTE: When this function is called, no pages may be locked in
  331. the cache for the specified view.
  332. Arguments:
  333. BaseAddress - Supplies the base address of the section in the
  334. system cache.
  335. SectionToUnmap - Supplies a pointer to the section which the
  336. base address maps.
  337. AddToFront - Supplies TRUE if the unmapped pages should be
  338. added to the front of the standby list (i.e., their
  339. value in the cache is low). FALSE otherwise.
  340. Return Value:
  341. None.
  342. Environment:
  343. Kernel mode.
  344. --*/
  345. {
  346. ULONG Waited;
  347. PMMPTE PointerPte;
  348. PMMPFN Pfn1;
  349. PMMPFN Pfn2;
  350. PMMPTE FirstPte;
  351. PMMPTE ProtoPte;
  352. MMPTE ProtoPteContents;
  353. MMPTE PteContents;
  354. KIRQL OldIrql;
  355. KIRQL OldIrqlWs;
  356. PFN_NUMBER i;
  357. WSLE_NUMBER WorkingSetIndex;
  358. PCONTROL_AREA ControlArea;
  359. ULONG WsHeld;
  360. PFN_NUMBER PageFrameIndex;
  361. PFN_NUMBER PageTableFrameIndex;
  362. PMSUBSECTION MappedSubsection;
  363. PMSUBSECTION LastSubsection;
  364. PETHREAD CurrentThread;
  365. #if DBG
  366. PFN_NUMBER j;
  367. PMSUBSECTION SubsectionArray[X256K / PAGE_SIZE];
  368. PMMPTE PteArray[X256K / PAGE_SIZE];
  369. RtlZeroMemory (SubsectionArray, sizeof(SubsectionArray));
  370. RtlCopyMemory (PteArray, MiGetPteAddress (BaseAddress), sizeof (PteArray));
  371. #endif
  372. WsHeld = FALSE;
  373. //
  374. // Initializing OldIrqlWs is not needed for correctness
  375. // but without it the compiler cannot compile this code
  376. // W4 to check for use of uninitialized variables.
  377. //
  378. OldIrqlWs = 0x99;
  379. CurrentThread = PsGetCurrentThread ();
  380. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  381. PointerPte = MiGetPteAddress (BaseAddress);
  382. FirstPte = PointerPte;
  383. PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress (PointerPte));
  384. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  385. //
  386. // Get the control area for the segment which is mapped here.
  387. //
  388. ControlArea = ((PSECTION)SectionToUnmap)->Segment->ControlArea;
  389. LastSubsection = NULL;
  390. ASSERT ((ControlArea->u.Flags.Image == 0) &&
  391. (ControlArea->u.Flags.PhysicalMemory == 0));
  392. i = 0;
  393. do {
  394. //
  395. // The cache is organized in chunks of 256k bytes, clear
  396. // the first chunk then check to see if this is the last
  397. // chunk.
  398. //
  399. // The page table page is always resident for the system cache.
  400. // Check each PTE: it is in one of three states, either valid or
  401. // prototype PTE format or zero.
  402. //
  403. PteContents = *(volatile MMPTE *)PointerPte;
  404. if (PteContents.u.Hard.Valid == 1) {
  405. if (!WsHeld) {
  406. WsHeld = TRUE;
  407. LOCK_SYSTEM_WS (OldIrqlWs, CurrentThread);
  408. continue;
  409. }
  410. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(&PteContents);
  411. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  412. WorkingSetIndex = MiLocateWsle (BaseAddress,
  413. MmSystemCacheWorkingSetList,
  414. Pfn1->u1.WsIndex);
  415. MiRemoveWsle (WorkingSetIndex,
  416. MmSystemCacheWorkingSetList);
  417. MiReleaseWsle (WorkingSetIndex, &MmSystemCacheWs);
  418. MI_SET_PTE_IN_WORKING_SET (PointerPte, 0);
  419. //
  420. // The PTE is valid.
  421. //
  422. //
  423. // Decrement the view count for every subsection this view spans.
  424. // But make sure it's only done once per subsection in a given view.
  425. //
  426. // The subsections can only be decremented after all the
  427. // PTEs have been cleared and PFN sharecounts decremented so no
  428. // prototype PTEs will be valid if it is indeed the final subsection
  429. // dereference. This is critical so the dereference segment
  430. // thread doesn't free pool containing valid prototype PTEs.
  431. //
  432. if (ControlArea->FilePointer != NULL) {
  433. ASSERT (Pfn1->u3.e1.PrototypePte);
  434. ASSERT (Pfn1->OriginalPte.u.Soft.Prototype);
  435. if ((LastSubsection != NULL) &&
  436. (Pfn1->PteAddress >= LastSubsection->SubsectionBase) &&
  437. (Pfn1->PteAddress < LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection)) {
  438. NOTHING;
  439. }
  440. else {
  441. MappedSubsection = (PMSUBSECTION)MiGetSubsectionAddress (&Pfn1->OriginalPte);
  442. if (MappedSubsection->ControlArea != ControlArea) {
  443. KeBugCheckEx (MEMORY_MANAGEMENT,
  444. 0x780,
  445. (ULONG_PTR) PointerPte,
  446. (ULONG_PTR) Pfn1,
  447. (ULONG_PTR) Pfn1->OriginalPte.u.Long);
  448. }
  449. ASSERT ((MappedSubsection->NumberOfMappedViews >= 1) ||
  450. (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 1));
  451. if (LastSubsection != MappedSubsection) {
  452. if (LastSubsection != NULL) {
  453. #if DBG
  454. for (j = 0; j < i; j += 1) {
  455. ASSERT (SubsectionArray[j] != MappedSubsection);
  456. }
  457. SubsectionArray[i] = MappedSubsection;
  458. #endif
  459. LOCK_PFN (OldIrql);
  460. MiRemoveViewsFromSection (LastSubsection,
  461. LastSubsection->PtesInSubsection);
  462. UNLOCK_PFN (OldIrql);
  463. }
  464. LastSubsection = MappedSubsection;
  465. }
  466. }
  467. }
  468. LOCK_PFN (OldIrql);
  469. //
  470. // Capture the state of the modified bit for this PTE.
  471. //
  472. MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
  473. //
  474. // Decrement the share and valid counts of the page table
  475. // page which maps this PTE.
  476. //
  477. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  478. //
  479. // Decrement the share count for the physical page.
  480. //
  481. #if DBG
  482. if (ControlArea->NumberOfMappedViews == 1) {
  483. ASSERT (Pfn1->u2.ShareCount == 1);
  484. }
  485. #endif
  486. MmFrontOfList = AddToFront;
  487. MiDecrementShareCountInline (Pfn1, PageFrameIndex);
  488. MmFrontOfList = FALSE;
  489. UNLOCK_PFN (OldIrql);
  490. }
  491. else {
  492. ASSERT ((PteContents.u.Long == ZeroKernelPte.u.Long) ||
  493. (PteContents.u.Soft.Prototype == 1));
  494. if (PteContents.u.Soft.Prototype == 1) {
  495. //
  496. // Decrement the view count for every subsection this view
  497. // spans. But make sure it's only done once per subsection
  498. // in a given view.
  499. //
  500. if (ControlArea->FilePointer != NULL) {
  501. ProtoPte = MiPteToProto (&PteContents);
  502. if ((LastSubsection != NULL) &&
  503. (ProtoPte >= LastSubsection->SubsectionBase) &&
  504. (ProtoPte < LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection)) {
  505. NOTHING;
  506. }
  507. else {
  508. LOCK_PFN (OldIrql);
  509. //
  510. // PTE is not valid, check the state of the prototype PTE.
  511. //
  512. if (WsHeld) {
  513. Waited = MiMakeSystemAddressValidPfnSystemWs (ProtoPte);
  514. }
  515. else {
  516. Waited = MiMakeSystemAddressValidPfn (ProtoPte);
  517. }
  518. if (Waited != 0) {
  519. //
  520. // Page fault occurred, recheck state of original PTE.
  521. //
  522. UNLOCK_PFN (OldIrql);
  523. continue;
  524. }
  525. ProtoPteContents = *ProtoPte;
  526. if (ProtoPteContents.u.Hard.Valid == 1) {
  527. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&ProtoPteContents);
  528. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  529. ProtoPte = &Pfn1->OriginalPte;
  530. }
  531. else if ((ProtoPteContents.u.Soft.Transition == 1) &&
  532. (ProtoPteContents.u.Soft.Prototype == 0)) {
  533. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&ProtoPteContents);
  534. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  535. ProtoPte = &Pfn1->OriginalPte;
  536. }
  537. else {
  538. Pfn1 = NULL;
  539. ASSERT (ProtoPteContents.u.Soft.Prototype == 1);
  540. }
  541. MappedSubsection = (PMSUBSECTION)MiGetSubsectionAddress (ProtoPte);
  542. if (MappedSubsection->ControlArea != ControlArea) {
  543. KeBugCheckEx (MEMORY_MANAGEMENT,
  544. 0x781,
  545. (ULONG_PTR) PointerPte,
  546. (ULONG_PTR) Pfn1,
  547. (ULONG_PTR) ProtoPte);
  548. }
  549. ASSERT ((MappedSubsection->NumberOfMappedViews >= 1) ||
  550. (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 1));
  551. if (LastSubsection != MappedSubsection) {
  552. if (LastSubsection != NULL) {
  553. #if DBG
  554. for (j = 0; j < i; j += 1) {
  555. ASSERT (SubsectionArray[j] != MappedSubsection);
  556. }
  557. SubsectionArray[i] = MappedSubsection;
  558. #endif
  559. MiRemoveViewsFromSection (LastSubsection,
  560. LastSubsection->PtesInSubsection);
  561. }
  562. LastSubsection = MappedSubsection;
  563. }
  564. UNLOCK_PFN (OldIrql);
  565. }
  566. }
  567. }
  568. if (WsHeld) {
  569. UNLOCK_SYSTEM_WS (OldIrqlWs);
  570. WsHeld = FALSE;
  571. }
  572. }
  573. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  574. PointerPte += 1;
  575. BaseAddress = (PVOID)((PCHAR)BaseAddress + PAGE_SIZE);
  576. i += 1;
  577. } while (i < (X256K / PAGE_SIZE));
  578. if (WsHeld) {
  579. UNLOCK_SYSTEM_WS (OldIrqlWs);
  580. }
  581. FirstPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  582. (FirstPte+1)->u.List.NextEntry = (KeReadTbFlushTimeStamp() & MM_FLUSH_COUNTER_MASK);
  583. LOCK_PFN (OldIrql);
  584. //
  585. // Free this entry to the end of the list.
  586. //
  587. MmLastFreeSystemCache->u.List.NextEntry = FirstPte - MmSystemCachePteBase;
  588. MmLastFreeSystemCache = FirstPte;
  589. if (LastSubsection != NULL) {
  590. MiRemoveViewsFromSection (LastSubsection,
  591. LastSubsection->PtesInSubsection);
  592. }
  593. //
  594. // Decrement the number of mapped views for the segment
  595. // and check to see if the segment should be deleted.
  596. //
  597. ControlArea->NumberOfMappedViews -= 1;
  598. ControlArea->NumberOfSystemCacheViews -= 1;
  599. //
  600. // Check to see if the control area (segment) should be deleted.
  601. // This routine releases the PFN lock.
  602. //
  603. MiCheckControlArea (ControlArea, NULL, OldIrql);
  604. return;
  605. }
  606. VOID
  607. MiRemoveMappedPtes (
  608. IN PVOID BaseAddress,
  609. IN ULONG NumberOfPtes,
  610. IN PCONTROL_AREA ControlArea,
  611. IN PMMSUPPORT WorkingSetInfo
  612. )
  613. /*++
  614. Routine Description:
  615. This function unmaps a view from the system cache or a session space.
  616. NOTE: When this function is called, no pages may be locked in
  617. the cache (or session space) for the specified view.
  618. Arguments:
  619. BaseAddress - Supplies the base address of the section in the
  620. system cache or session space.
  621. NumberOfPtes - Supplies the number of PTEs to unmap.
  622. ControlArea - Supplies the control area mapping the view.
  623. WorkingSetInfo - Supplies the charged working set structures.
  624. Return Value:
  625. None.
  626. Environment:
  627. Kernel mode.
  628. This routine could be made PAGELK but it is a high frequency routine
  629. so it is actually better to keep it nonpaged to avoid bringing in the
  630. entire PAGELK section.
  631. --*/
  632. {
  633. ULONG Waited;
  634. PMMPTE PointerPte;
  635. PMMPTE PointerPde;
  636. PMMPFN Pfn1;
  637. PMMPTE FirstPte;
  638. PMMPTE ProtoPte;
  639. MMPTE PteContents;
  640. KIRQL OldIrql;
  641. KIRQL OldIrqlWs;
  642. WSLE_NUMBER WorkingSetIndex;
  643. ULONG DereferenceSegment;
  644. MMPTE_FLUSH_LIST PteFlushList;
  645. MMPTE ProtoPteContents;
  646. PFN_NUMBER PageFrameIndex;
  647. ULONG WsHeld;
  648. PMMPFN Pfn2;
  649. PFN_NUMBER PageTableFrameIndex;
  650. PMSUBSECTION MappedSubsection;
  651. PMSUBSECTION LastSubsection;
  652. PETHREAD CurrentThread;
  653. CurrentThread = PsGetCurrentThread ();
  654. DereferenceSegment = FALSE;
  655. WsHeld = FALSE;
  656. LastSubsection = NULL;
  657. PteFlushList.Count = 0;
  658. PointerPte = MiGetPteAddress (BaseAddress);
  659. FirstPte = PointerPte;
  660. //
  661. // Initializing OldIrqlWs is not needed for correctness
  662. // but without it the compiler cannot compile this code
  663. // W4 to check for use of uninitialized variables.
  664. //
  665. OldIrqlWs = 0x99;
  666. //
  667. // Get the control area for the segment which is mapped here.
  668. //
  669. while (NumberOfPtes) {
  670. //
  671. // The page table page is always resident for the system space (and
  672. // for a session space) map.
  673. //
  674. // Check each PTE, it is in one of two states, either valid or
  675. // prototype PTE format.
  676. //
  677. PteContents = *PointerPte;
  678. if (PteContents.u.Hard.Valid == 1) {
  679. //
  680. // The system cache is locked by us, all others are locked by
  681. // the caller.
  682. //
  683. if (WorkingSetInfo == &MmSystemCacheWs) {
  684. if (!WsHeld) {
  685. WsHeld = TRUE;
  686. LOCK_SYSTEM_WS (OldIrqlWs, CurrentThread);
  687. continue;
  688. }
  689. }
  690. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  691. WorkingSetIndex = MiLocateWsle (BaseAddress,
  692. WorkingSetInfo->VmWorkingSetList,
  693. Pfn1->u1.WsIndex);
  694. ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
  695. MiRemoveWsle (WorkingSetIndex,
  696. WorkingSetInfo->VmWorkingSetList);
  697. MiReleaseWsle (WorkingSetIndex, WorkingSetInfo);
  698. MI_SET_PTE_IN_WORKING_SET (PointerPte, 0);
  699. LOCK_PFN (OldIrql);
  700. //
  701. // The PTE is valid.
  702. //
  703. //
  704. // Decrement the view count for every subsection this view spans.
  705. // But make sure it's only done once per subsection in a given view.
  706. //
  707. // The subsections can only be decremented after all the
  708. // PTEs have been cleared and PFN sharecounts decremented so no
  709. // prototype PTEs will be valid if it is indeed the final subsection
  710. // dereference. This is critical so the dereference segment
  711. // thread doesn't free pool containing valid prototype PTEs.
  712. //
  713. if ((Pfn1->u3.e1.PrototypePte) &&
  714. (Pfn1->OriginalPte.u.Soft.Prototype)) {
  715. if ((LastSubsection != NULL) &&
  716. (Pfn1->PteAddress >= LastSubsection->SubsectionBase) &&
  717. (Pfn1->PteAddress < LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection)) {
  718. NOTHING;
  719. }
  720. else {
  721. MappedSubsection = (PMSUBSECTION)MiGetSubsectionAddress (&Pfn1->OriginalPte);
  722. if (LastSubsection != MappedSubsection) {
  723. ASSERT (ControlArea == MappedSubsection->ControlArea);
  724. if ((ControlArea->FilePointer != NULL) &&
  725. (ControlArea->u.Flags.Image == 0) &&
  726. (ControlArea->u.Flags.PhysicalMemory == 0)) {
  727. if (LastSubsection != NULL) {
  728. MiRemoveViewsFromSection (LastSubsection,
  729. LastSubsection->PtesInSubsection);
  730. }
  731. LastSubsection = MappedSubsection;
  732. }
  733. }
  734. }
  735. }
  736. //
  737. // Capture the state of the modified bit for this PTE.
  738. //
  739. MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
  740. //
  741. // Flush the TB for this page.
  742. //
  743. if (PteFlushList.Count != MM_MAXIMUM_FLUSH_COUNT) {
  744. PteFlushList.FlushPte[PteFlushList.Count] = PointerPte;
  745. PteFlushList.FlushVa[PteFlushList.Count] = BaseAddress;
  746. PteFlushList.Count += 1;
  747. }
  748. PointerPde = MiGetPteAddress (PointerPte);
  749. #if (_MI_PAGING_LEVELS < 3)
  750. //
  751. // The PDE must be carefully checked against the master table
  752. // because the PDEs are all zeroed in process creation. If this
  753. // process has never faulted on any address in this range (all
  754. // references prior and above were filled directly by the TB as
  755. // the PTEs are global on non-Hydra), then the PDE reference
  756. // below to determine the page table frame will be zero.
  757. //
  758. // Note this cannot happen on NT64 as no master table is used.
  759. //
  760. if (PointerPde->u.Long == 0) {
  761. PMMPTE MasterPde;
  762. MasterPde = &MmSystemPagePtes [((ULONG_PTR)PointerPde &
  763. (PD_PER_SYSTEM * (sizeof(MMPTE) * PDE_PER_PAGE) - 1)) / sizeof(MMPTE)];
  764. ASSERT (MasterPde->u.Hard.Valid == 1);
  765. MI_WRITE_VALID_PTE (PointerPde, *MasterPde);
  766. }
  767. #endif
  768. //
  769. // Decrement the share and valid counts of the page table
  770. // page which maps this PTE.
  771. //
  772. PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
  773. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  774. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  775. //
  776. // Decrement the share count for the physical page.
  777. //
  778. MiDecrementShareCount (MI_GET_PAGE_FRAME_FROM_PTE (&PteContents));
  779. UNLOCK_PFN (OldIrql);
  780. }
  781. else {
  782. if (WorkingSetInfo == &MmSystemCacheWs) {
  783. if (WsHeld) {
  784. UNLOCK_SYSTEM_WS (OldIrqlWs);
  785. WsHeld = FALSE;
  786. }
  787. }
  788. ASSERT ((PteContents.u.Long == ZeroKernelPte.u.Long) ||
  789. (PteContents.u.Soft.Prototype == 1));
  790. if (PteContents.u.Soft.Prototype == 1) {
  791. //
  792. // Decrement the view count for every subsection this view
  793. // spans. But make sure it's only done once per subsection
  794. // in a given view.
  795. //
  796. ProtoPte = MiPteToProto (&PteContents);
  797. if ((LastSubsection != NULL) &&
  798. (ProtoPte >= LastSubsection->SubsectionBase) &&
  799. (ProtoPte < LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection)) {
  800. NOTHING;
  801. }
  802. else {
  803. //
  804. // PTE is not valid, check the state of the prototype PTE.
  805. //
  806. LOCK_PFN (OldIrql);
  807. if (WsHeld) {
  808. Waited = MiMakeSystemAddressValidPfnSystemWs (ProtoPte);
  809. }
  810. else {
  811. Waited = MiMakeSystemAddressValidPfn (ProtoPte);
  812. }
  813. if (Waited != 0) {
  814. //
  815. // Page fault occurred, recheck state of original PTE.
  816. //
  817. UNLOCK_PFN (OldIrql);
  818. continue;
  819. }
  820. ProtoPteContents = *ProtoPte;
  821. if (ProtoPteContents.u.Hard.Valid == 1) {
  822. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&ProtoPteContents);
  823. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  824. ProtoPte = &Pfn1->OriginalPte;
  825. if (ProtoPte->u.Soft.Prototype == 0) {
  826. ProtoPte = NULL;
  827. }
  828. }
  829. else if ((ProtoPteContents.u.Soft.Transition == 1) &&
  830. (ProtoPteContents.u.Soft.Prototype == 0)) {
  831. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&ProtoPteContents);
  832. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  833. ProtoPte = &Pfn1->OriginalPte;
  834. if (ProtoPte->u.Soft.Prototype == 0) {
  835. ProtoPte = NULL;
  836. }
  837. }
  838. else if (ProtoPteContents.u.Soft.Prototype == 1) {
  839. NOTHING;
  840. }
  841. else {
  842. //
  843. // Could be a zero PTE or a demand zero PTE.
  844. // Neither belong to a mapped file.
  845. //
  846. ProtoPte = NULL;
  847. }
  848. if (ProtoPte != NULL) {
  849. MappedSubsection = (PMSUBSECTION)MiGetSubsectionAddress (ProtoPte);
  850. if (LastSubsection != MappedSubsection) {
  851. ASSERT (ControlArea == MappedSubsection->ControlArea);
  852. if ((ControlArea->FilePointer != NULL) &&
  853. (ControlArea->u.Flags.Image == 0) &&
  854. (ControlArea->u.Flags.PhysicalMemory == 0)) {
  855. if (LastSubsection != NULL) {
  856. MiRemoveViewsFromSection (LastSubsection,
  857. LastSubsection->PtesInSubsection);
  858. }
  859. LastSubsection = MappedSubsection;
  860. }
  861. }
  862. }
  863. UNLOCK_PFN (OldIrql);
  864. }
  865. }
  866. }
  867. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  868. PointerPte += 1;
  869. BaseAddress = (PVOID)((PCHAR)BaseAddress + PAGE_SIZE);
  870. NumberOfPtes -= 1;
  871. }
  872. if (WorkingSetInfo == &MmSystemCacheWs) {
  873. if (WsHeld) {
  874. UNLOCK_SYSTEM_WS (OldIrqlWs);
  875. }
  876. }
  877. LOCK_PFN (OldIrql);
  878. if (LastSubsection != NULL) {
  879. MiRemoveViewsFromSection (LastSubsection,
  880. LastSubsection->PtesInSubsection);
  881. }
  882. MiFlushPteList (&PteFlushList, TRUE, ZeroKernelPte);
  883. if (WorkingSetInfo != &MmSystemCacheWs) {
  884. //
  885. // Session space has no ASN - flush the entire TB.
  886. //
  887. MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE);
  888. }
  889. //
  890. // Decrement the number of user references as the caller upped them
  891. // via MiCheckPurgeAndUpMapCount when this was originally mapped.
  892. //
  893. ControlArea->NumberOfUserReferences -= 1;
  894. //
  895. // Decrement the number of mapped views for the segment
  896. // and check to see if the segment should be deleted.
  897. //
  898. ControlArea->NumberOfMappedViews -= 1;
  899. //
  900. // Check to see if the control area (segment) should be deleted.
  901. // This routine releases the PFN lock.
  902. //
  903. MiCheckControlArea (ControlArea, NULL, OldIrql);
  904. }
  905. VOID
  906. MiInitializeSystemCache (
  907. IN ULONG MinimumWorkingSet,
  908. IN ULONG MaximumWorkingSet
  909. )
  910. /*++
  911. Routine Description:
  912. This routine initializes the system cache working set and
  913. data management structures.
  914. Arguments:
  915. MinimumWorkingSet - Supplies the minimum working set for the system
  916. cache.
  917. MaximumWorkingSet - Supplies the maximum working set size for the
  918. system cache.
  919. Return Value:
  920. None.
  921. Environment:
  922. Kernel mode, called only at phase 0 initialization.
  923. --*/
  924. {
  925. ULONG_PTR SizeOfSystemCacheInPages;
  926. ULONG_PTR HunksOf256KInCache;
  927. PMMWSLE WslEntry;
  928. ULONG NumberOfEntriesMapped;
  929. PFN_NUMBER i;
  930. MMPTE PteContents;
  931. PMMPTE PointerPte;
  932. PMMPTE PointerPde;
  933. KIRQL OldIrql;
  934. PointerPte = MiGetPteAddress (MmSystemCacheWorkingSetList);
  935. PteContents = ValidKernelPte;
  936. LOCK_PFN (OldIrql);
  937. i = MiRemoveZeroPage(MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  938. PteContents.u.Hard.PageFrameNumber = i;
  939. MI_WRITE_VALID_PTE (PointerPte, PteContents);
  940. MiInitializePfn (i, PointerPte, 1L);
  941. UNLOCK_PFN (OldIrql);
  942. #if defined (_WIN64)
  943. MmSystemCacheWsle = (PMMWSLE)(MmSystemCacheWorkingSetList + 1);
  944. #else
  945. MmSystemCacheWsle =
  946. (PMMWSLE)(&MmSystemCacheWorkingSetList->UsedPageTableEntries[0]);
  947. #endif
  948. MmSystemCacheWs.VmWorkingSetList = MmSystemCacheWorkingSetList;
  949. MmSystemCacheWs.WorkingSetSize = 0;
  950. MmSystemCacheWs.MinimumWorkingSetSize = MinimumWorkingSet;
  951. MmSystemCacheWs.MaximumWorkingSetSize = MaximumWorkingSet;
  952. InsertTailList (&MmWorkingSetExpansionHead.ListHead,
  953. &MmSystemCacheWs.WorkingSetExpansionLinks);
  954. MmSystemCacheWs.Flags.AllowWorkingSetAdjustment = TRUE;
  955. //
  956. // Don't use entry 0 as an index of zero in the PFN database
  957. // means that the page can be assigned to a slot. This is not
  958. // a problem for process working sets as page 0 is private.
  959. //
  960. MmSystemCacheWorkingSetList->FirstFree = 1;
  961. MmSystemCacheWorkingSetList->FirstDynamic = 1;
  962. MmSystemCacheWorkingSetList->NextSlot = 1;
  963. MmSystemCacheWorkingSetList->LastEntry = (ULONG)MmSystemCacheWsMinimum;
  964. MmSystemCacheWorkingSetList->HashTable = NULL;
  965. MmSystemCacheWorkingSetList->HashTableSize = 0;
  966. MmSystemCacheWorkingSetList->Wsle = MmSystemCacheWsle;
  967. MmSystemCacheWorkingSetList->HashTableStart =
  968. (PVOID)((PCHAR)PAGE_ALIGN (&MmSystemCacheWorkingSetList->Wsle[MM_MAXIMUM_WORKING_SET]) + PAGE_SIZE);
  969. MmSystemCacheWorkingSetList->HighestPermittedHashAddress = (PVOID)(MM_SYSTEM_CACHE_START);
  970. NumberOfEntriesMapped = (ULONG)(((PMMWSLE)((PCHAR)MmSystemCacheWorkingSetList +
  971. PAGE_SIZE)) - MmSystemCacheWsle);
  972. LOCK_PFN (OldIrql);
  973. while (NumberOfEntriesMapped < MmSystemCacheWsMaximum) {
  974. PointerPte += 1;
  975. if (MiIsPteOnPdeBoundary(PointerPte)) {
  976. PointerPde = MiGetPteAddress(PointerPte);
  977. if (PointerPde->u.Hard.Valid == 0) {
  978. i = MiRemoveZeroPage(MI_GET_PAGE_COLOR_FROM_PTE (PointerPde));
  979. PteContents.u.Hard.PageFrameNumber = i;
  980. MI_WRITE_VALID_PTE(PointerPde, PteContents);
  981. MiInitializePfn (i, PointerPde, 1L);
  982. }
  983. }
  984. i = MiRemoveZeroPage(MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  985. PteContents.u.Hard.PageFrameNumber = i;
  986. MI_WRITE_VALID_PTE (PointerPte, PteContents);
  987. MiInitializePfn (i, PointerPte, 1L);
  988. NumberOfEntriesMapped += PAGE_SIZE / sizeof(MMWSLE);
  989. }
  990. UNLOCK_PFN (OldIrql);
  991. //
  992. // Initialize the following slots as free.
  993. //
  994. WslEntry = MmSystemCacheWsle + 1;
  995. for (i = 1; i < NumberOfEntriesMapped; i++) {
  996. //
  997. // Build the free list, note that the first working
  998. // set entries (CurrentEntry) are not on the free list.
  999. // These entries are reserved for the pages which
  1000. // map the working set and the page which contains the PDE.
  1001. //
  1002. WslEntry->u1.Long = (i + 1) << MM_FREE_WSLE_SHIFT;
  1003. WslEntry += 1;
  1004. }
  1005. WslEntry -= 1;
  1006. WslEntry->u1.Long = WSLE_NULL_INDEX << MM_FREE_WSLE_SHIFT; // End of list.
  1007. MmSystemCacheWorkingSetList->LastInitializedWsle = NumberOfEntriesMapped - 1;
  1008. //
  1009. // Build a free list structure in the PTEs for the system cache.
  1010. //
  1011. MmSystemCachePteBase = MI_PTE_BASE_FOR_LOWEST_KERNEL_ADDRESS;
  1012. SizeOfSystemCacheInPages = MI_COMPUTE_PAGES_SPANNED (MmSystemCacheStart,
  1013. (PCHAR)MmSystemCacheEnd - (PCHAR)MmSystemCacheStart + 1);
  1014. HunksOf256KInCache = SizeOfSystemCacheInPages / (X256K / PAGE_SIZE);
  1015. PointerPte = MiGetPteAddress (MmSystemCacheStart);
  1016. MmFirstFreeSystemCache = PointerPte;
  1017. for (i = 0; i < HunksOf256KInCache; i += 1) {
  1018. PointerPte->u.List.NextEntry = (PointerPte + (X256K / PAGE_SIZE)) - MmSystemCachePteBase;
  1019. PointerPte += X256K / PAGE_SIZE;
  1020. }
  1021. PointerPte -= X256K / PAGE_SIZE;
  1022. #if defined(_X86_)
  1023. //
  1024. // Add any extended ranges.
  1025. //
  1026. if (MiSystemCacheEndExtra != MmSystemCacheEnd) {
  1027. SizeOfSystemCacheInPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (MiSystemCacheStartExtra,
  1028. (PCHAR)MiSystemCacheEndExtra - (PCHAR)MiSystemCacheStartExtra + 1);
  1029. HunksOf256KInCache = SizeOfSystemCacheInPages / (X256K / PAGE_SIZE);
  1030. if (HunksOf256KInCache) {
  1031. PMMPTE PointerPteExtended;
  1032. PointerPteExtended = MiGetPteAddress (MiSystemCacheStartExtra);
  1033. PointerPte->u.List.NextEntry = PointerPteExtended - MmSystemCachePteBase;
  1034. PointerPte = PointerPteExtended;
  1035. for (i = 0; i < HunksOf256KInCache; i += 1) {
  1036. PointerPte->u.List.NextEntry = (PointerPte + (X256K / PAGE_SIZE)) - MmSystemCachePteBase;
  1037. PointerPte += X256K / PAGE_SIZE;
  1038. }
  1039. PointerPte -= X256K / PAGE_SIZE;
  1040. }
  1041. }
  1042. #endif
  1043. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  1044. MmLastFreeSystemCache = PointerPte;
  1045. if (MaximumWorkingSet > ((1536*1024) >> PAGE_SHIFT)) {
  1046. //
  1047. // The working set list consists of more than a single page.
  1048. //
  1049. LOCK_SYSTEM_WS (OldIrql, PsGetCurrentThread ());
  1050. MiGrowWsleHash (&MmSystemCacheWs);
  1051. UNLOCK_SYSTEM_WS (OldIrql);
  1052. }
  1053. }
  1054. BOOLEAN
  1055. MmCheckCachedPageState (
  1056. IN PVOID SystemCacheAddress,
  1057. IN BOOLEAN SetToZero
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. This routine checks the state of the specified page that is mapped in
  1062. the system cache. If the specified virtual address can be made valid
  1063. (i.e., the page is already in memory), it is made valid and the value
  1064. TRUE is returned.
  1065. If the page is not in memory, and SetToZero is FALSE, the
  1066. value FALSE is returned. However, if SetToZero is TRUE, a page of
  1067. zeroes is materialized for the specified virtual address and the address
  1068. is made valid and the value TRUE is returned.
  1069. This routine is for usage by the cache manager.
  1070. Arguments:
  1071. SystemCacheAddress - Supplies the address of a page mapped in the
  1072. system cache.
  1073. SetToZero - Supplies TRUE if a page of zeroes should be created in the
  1074. case where no page is already mapped.
  1075. Return Value:
  1076. FALSE if touching this page would cause a page fault resulting
  1077. in a page read.
  1078. TRUE if there is a physical page in memory for this address.
  1079. Environment:
  1080. Kernel mode.
  1081. --*/
  1082. {
  1083. ULONG Flags;
  1084. PMMPTE PointerPte;
  1085. PMMPTE PointerPde;
  1086. PMMPTE ProtoPte;
  1087. PFN_NUMBER PageFrameIndex;
  1088. WSLE_NUMBER WorkingSetIndex;
  1089. MMPTE TempPte;
  1090. MMPTE ProtoPteContents;
  1091. PMMPFN Pfn1;
  1092. PMMPFN Pfn2;
  1093. KIRQL OldIrql;
  1094. LOGICAL BarrierNeeded;
  1095. ULONG BarrierStamp;
  1096. PSUBSECTION Subsection;
  1097. PFILE_OBJECT FileObject;
  1098. LONGLONG FileOffset;
  1099. PointerPte = MiGetPteAddress (SystemCacheAddress);
  1100. //
  1101. // Make the PTE valid if possible.
  1102. //
  1103. if (PointerPte->u.Hard.Valid == 1) {
  1104. return TRUE;
  1105. }
  1106. BarrierNeeded = FALSE;
  1107. Subsection = NULL;
  1108. //
  1109. // Initializing BarrierStamp is not needed for
  1110. // correctness but without it the compiler cannot compile this code
  1111. // W4 to check for use of uninitialized variables.
  1112. //
  1113. BarrierStamp = 0;
  1114. LOCK_PFN (OldIrql);
  1115. if (PointerPte->u.Hard.Valid == 1) {
  1116. goto UnlockAndReturnTrue;
  1117. }
  1118. ASSERT (PointerPte->u.Soft.Prototype == 1);
  1119. ProtoPte = MiPteToProto (PointerPte);
  1120. //
  1121. // PTE is not valid, check the state of the prototype PTE.
  1122. //
  1123. if (MiMakeSystemAddressValidPfn (ProtoPte)) {
  1124. //
  1125. // If page fault occurred, recheck state of original PTE.
  1126. //
  1127. if (PointerPte->u.Hard.Valid == 1) {
  1128. goto UnlockAndReturnTrue;
  1129. }
  1130. }
  1131. ProtoPteContents = *ProtoPte;
  1132. if (ProtoPteContents.u.Hard.Valid == 1) {
  1133. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&ProtoPteContents);
  1134. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1135. //
  1136. // The prototype PTE is valid, make the cache PTE
  1137. // valid and add it to the working set.
  1138. //
  1139. TempPte = ProtoPteContents;
  1140. }
  1141. else if ((ProtoPteContents.u.Soft.Transition == 1) &&
  1142. (ProtoPteContents.u.Soft.Prototype == 0)) {
  1143. //
  1144. // Prototype PTE is in the transition state. Remove the page
  1145. // from the page list and make it valid.
  1146. //
  1147. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&ProtoPteContents);
  1148. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1149. if ((Pfn1->u3.e1.ReadInProgress) || (Pfn1->u4.InPageError)) {
  1150. //
  1151. // Collided page fault, return.
  1152. //
  1153. goto UnlockAndReturnTrue;
  1154. }
  1155. if (MmAvailablePages == 0) {
  1156. //
  1157. // This can only happen if the system is utilizing
  1158. // a hardware compression cache. This ensures that
  1159. // only a safe amount of the compressed virtual cache
  1160. // is directly mapped so that if the hardware gets
  1161. // into trouble, we can bail it out.
  1162. //
  1163. // Just unlock everything here to give the compression
  1164. // reaper a chance to ravage pages and then retry.
  1165. //
  1166. goto UnlockAndReturnTrue;
  1167. }
  1168. MiUnlinkPageFromList (Pfn1);
  1169. Pfn1->u3.e2.ReferenceCount += 1;
  1170. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1171. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  1172. MI_SNAP_DATA (Pfn1, ProtoPte, 1);
  1173. MI_MAKE_VALID_PTE (TempPte,
  1174. PageFrameIndex,
  1175. Pfn1->OriginalPte.u.Soft.Protection,
  1176. NULL );
  1177. MI_WRITE_VALID_PTE (ProtoPte, TempPte);
  1178. //
  1179. // Increment the valid PTE count for the page containing
  1180. // the prototype PTE.
  1181. //
  1182. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  1183. }
  1184. else {
  1185. //
  1186. // Page is not in memory, if a page of zeroes is requested,
  1187. // get a page of zeroes and make it valid.
  1188. //
  1189. if ((SetToZero == FALSE) || (MmAvailablePages < 8)) {
  1190. UNLOCK_PFN (OldIrql);
  1191. //
  1192. // Fault the page into memory.
  1193. //
  1194. MmAccessFault (FALSE, SystemCacheAddress, KernelMode, (PVOID)0);
  1195. return FALSE;
  1196. }
  1197. //
  1198. // Increment the count of Pfn references for the control area
  1199. // corresponding to this file.
  1200. //
  1201. MiGetSubsectionAddress (
  1202. ProtoPte)->ControlArea->NumberOfPfnReferences += 1;
  1203. PageFrameIndex = MiRemoveZeroPage(MI_GET_PAGE_COLOR_FROM_PTE (ProtoPte));
  1204. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1205. //
  1206. // This barrier check is needed after zeroing the page and
  1207. // before setting the PTE (not the prototype PTE) valid.
  1208. // Capture it now, check it at the last possible moment.
  1209. //
  1210. BarrierNeeded = TRUE;
  1211. BarrierStamp = (ULONG)Pfn1->u4.PteFrame;
  1212. MiInitializePfn (PageFrameIndex, ProtoPte, 1);
  1213. Pfn1->u2.ShareCount = 0;
  1214. Pfn1->u3.e1.PrototypePte = 1;
  1215. MI_SNAP_DATA (Pfn1, ProtoPte, 2);
  1216. MI_MAKE_VALID_PTE (TempPte,
  1217. PageFrameIndex,
  1218. Pfn1->OriginalPte.u.Soft.Protection,
  1219. NULL );
  1220. MI_WRITE_VALID_PTE (ProtoPte, TempPte);
  1221. }
  1222. //
  1223. // Increment the share count since the page is being put into a working
  1224. // set.
  1225. //
  1226. Pfn1->u2.ShareCount += 1;
  1227. if (Pfn1->u1.Event == NULL) {
  1228. Pfn1->u1.Event = (PVOID)PsGetCurrentThread();
  1229. }
  1230. //
  1231. // Increment the reference count of the page table
  1232. // page for this PTE.
  1233. //
  1234. PointerPde = MiGetPteAddress (PointerPte);
  1235. Pfn2 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
  1236. Pfn2->u2.ShareCount += 1;
  1237. MI_SET_GLOBAL_STATE (TempPte, 1);
  1238. #if defined (_WIN64)
  1239. if (MI_DETERMINE_OWNER (PointerPte) == 0) {
  1240. TempPte.u.Long &= ~MM_PTE_OWNER_MASK;
  1241. }
  1242. #else
  1243. TempPte.u.Hard.Owner = MI_DETERMINE_OWNER (PointerPte);
  1244. #endif
  1245. if (BarrierNeeded) {
  1246. MI_BARRIER_SYNCHRONIZE (BarrierStamp);
  1247. }
  1248. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  1249. //
  1250. // Capture prefetch fault information.
  1251. //
  1252. if (CCPF_IS_PREFETCHER_ACTIVE()) {
  1253. TempPte = Pfn1->OriginalPte;
  1254. if (TempPte.u.Soft.Prototype == 1) {
  1255. Subsection = MiGetSubsectionAddress (&TempPte);
  1256. }
  1257. }
  1258. UNLOCK_PFN (OldIrql);
  1259. //
  1260. // Log prefetch fault information now that the PFN lock has been
  1261. // released and the PTE has been made valid. This minimizes PFN
  1262. // lock contention, allows CcPfLogPageFault to allocate (and fault on)
  1263. // pool, and allows other threads in this process to execute without
  1264. // faulting on this address.
  1265. //
  1266. if (Subsection != NULL) {
  1267. FileObject = Subsection->ControlArea->FilePointer;
  1268. FileOffset = MiStartingOffset (Subsection, ProtoPte);
  1269. Flags = 0;
  1270. ASSERT (Subsection->ControlArea->u.Flags.Image == 0);
  1271. if (Subsection->ControlArea->u.Flags.Rom) {
  1272. Flags |= CCPF_TYPE_ROM;
  1273. }
  1274. CcPfLogPageFault (FileObject, FileOffset, Flags);
  1275. }
  1276. LOCK_SYSTEM_WS (OldIrql, PsGetCurrentThread ());
  1277. WorkingSetIndex = MiLocateAndReserveWsle (&MmSystemCacheWs);
  1278. MiUpdateWsle (&WorkingSetIndex,
  1279. MiGetVirtualAddressMappedByPte (PointerPte),
  1280. MmSystemCacheWorkingSetList,
  1281. Pfn1);
  1282. MmSystemCacheWsle[WorkingSetIndex].u1.e1.SameProtectAsProto = 1;
  1283. MI_SET_PTE_IN_WORKING_SET (PointerPte, WorkingSetIndex);
  1284. UNLOCK_SYSTEM_WS (OldIrql);
  1285. return TRUE;
  1286. UnlockAndReturnTrue:
  1287. UNLOCK_PFN (OldIrql);
  1288. return TRUE;
  1289. }
  1290. NTSTATUS
  1291. MmCopyToCachedPage (
  1292. IN PVOID SystemCacheAddress,
  1293. IN PVOID UserBuffer,
  1294. IN ULONG Offset,
  1295. IN SIZE_T CountInBytes,
  1296. IN BOOLEAN DontZero
  1297. )
  1298. /*++
  1299. Routine Description:
  1300. This routine checks the state of the specified page that is mapped in
  1301. the system cache. If the specified virtual address can be made valid
  1302. (i.e., the page is already in memory), it is made valid and the value
  1303. TRUE is returned.
  1304. If the page is not in memory, and SetToZero is FALSE, the
  1305. value FALSE is returned. However, if SetToZero is TRUE, a page of
  1306. zeroes is materialized for the specified virtual address and the address
  1307. is made valid and the value TRUE is returned.
  1308. This routine is for usage by the cache manager.
  1309. Arguments:
  1310. SystemCacheAddress - Supplies the address of a page mapped in the system
  1311. cache. This MUST be a page aligned address!
  1312. UserBuffer - Supplies the address of a user buffer to copy into the
  1313. system cache at the specified address + offset.
  1314. Offset - Supplies the offset into the UserBuffer to copy the data.
  1315. CountInBytes - Supplies the byte count to copy from the user buffer.
  1316. DontZero - Supplies TRUE if the buffer should not be zeroed (the
  1317. caller will track zeroing). FALSE if it should be zeroed.
  1318. Return Value:
  1319. Returns the status of the copy.
  1320. Environment:
  1321. Kernel mode, <= APC_LEVEL.
  1322. --*/
  1323. {
  1324. ULONG Flags;
  1325. PMMPTE PointerPte;
  1326. PMMPTE PointerPde;
  1327. PMMPTE ProtoPte;
  1328. PFN_NUMBER PageFrameIndex;
  1329. WSLE_NUMBER WorkingSetIndex;
  1330. MMPTE TempPte;
  1331. MMPTE TempPte2;
  1332. MMPTE ProtoPteContents;
  1333. PMMPFN Pfn1;
  1334. PMMPFN Pfn2;
  1335. KIRQL OldIrql;
  1336. ULONG TransitionState;
  1337. ULONG AddToWorkingSet;
  1338. LOGICAL ShareCountUpped;
  1339. SIZE_T EndFill;
  1340. PVOID Buffer;
  1341. NTSTATUS status;
  1342. PMMINPAGE_SUPPORT Event;
  1343. PCONTROL_AREA ControlArea;
  1344. PETHREAD Thread;
  1345. ULONG SavedState;
  1346. LOGICAL ApcNeeded;
  1347. PKTHREAD CurrentThread;
  1348. PSUBSECTION Subsection;
  1349. PFILE_OBJECT FileObject;
  1350. LONGLONG FileOffset;
  1351. TransitionState = FALSE;
  1352. AddToWorkingSet = FALSE;
  1353. CurrentThread = NULL;
  1354. ApcNeeded = FALSE;
  1355. Subsection = NULL;
  1356. //
  1357. // Initializing these is not needed for correctness
  1358. // but without it the compiler cannot compile this code
  1359. // W4 to check for use of uninitialized variables.
  1360. //
  1361. ProtoPte = NULL;
  1362. ShareCountUpped = FALSE;
  1363. TempPte.u.Long = 0;
  1364. Event = NULL;
  1365. Pfn1 = NULL;
  1366. ASSERT (((ULONG_PTR)SystemCacheAddress & (PAGE_SIZE - 1)) == 0);
  1367. ASSERT ((CountInBytes + Offset) <= PAGE_SIZE);
  1368. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  1369. PointerPte = MiGetPteAddress (SystemCacheAddress);
  1370. if (PointerPte->u.Hard.Valid == 1) {
  1371. goto Copy;
  1372. }
  1373. //
  1374. // Touch the user's buffer to make it resident. This is required in
  1375. // order to safely detect the case where both the system and user
  1376. // address are pointing at the same physical page. This case causes
  1377. // a deadlock during the RtlCopyBytes if the inpage support block needed
  1378. // to be allocated and the PTE for the user page is not valid. This
  1379. // potential deadlock is resolved because if the user page causes a
  1380. // collided fault, the initiator thread is checked for. If they are
  1381. // the same, then an exception is thrown by the pager.
  1382. //
  1383. try {
  1384. *(volatile CHAR *)UserBuffer;
  1385. } except (EXCEPTION_EXECUTE_HANDLER) {
  1386. return GetExceptionCode();
  1387. }
  1388. //
  1389. // Make the PTE valid if possible.
  1390. //
  1391. LOCK_PFN (OldIrql);
  1392. Recheck:
  1393. if (PointerPte->u.Hard.Valid == 1) {
  1394. goto UnlockAndCopy;
  1395. }
  1396. ASSERT (PointerPte->u.Soft.Prototype == 1);
  1397. ProtoPte = MiPteToProto (PointerPte);
  1398. //
  1399. // Pte is not valid, check the state of the prototype PTE.
  1400. //
  1401. if (MiMakeSystemAddressValidPfn (ProtoPte)) {
  1402. //
  1403. // If page fault occurred, recheck state of original PTE.
  1404. //
  1405. if (PointerPte->u.Hard.Valid == 1) {
  1406. goto UnlockAndCopy;
  1407. }
  1408. }
  1409. ShareCountUpped = FALSE;
  1410. ProtoPteContents = *ProtoPte;
  1411. if (ProtoPteContents.u.Hard.Valid == 1) {
  1412. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&ProtoPteContents);
  1413. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1414. //
  1415. // Increment the share count so the prototype PTE will remain
  1416. // valid until this can be added into the system's working set.
  1417. //
  1418. Pfn1->u2.ShareCount += 1;
  1419. ShareCountUpped = TRUE;
  1420. //
  1421. // The prototype PTE is valid, make the cache PTE
  1422. // valid and add it to the working set.
  1423. //
  1424. TempPte = ProtoPteContents;
  1425. }
  1426. else if ((ProtoPteContents.u.Soft.Transition == 1) &&
  1427. (ProtoPteContents.u.Soft.Prototype == 0)) {
  1428. //
  1429. // Prototype PTE is in the transition state. Remove the page
  1430. // from the page list and make it valid.
  1431. //
  1432. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&ProtoPteContents);
  1433. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1434. if ((Pfn1->u3.e1.ReadInProgress) || (Pfn1->u4.InPageError)) {
  1435. //
  1436. // Collided page fault or in page error, try the copy
  1437. // operation incurring a page fault.
  1438. //
  1439. goto UnlockAndCopy;
  1440. }
  1441. ASSERT ((SPFN_NUMBER)MmAvailablePages >= 0);
  1442. if (MmAvailablePages == 0) {
  1443. //
  1444. // This can only happen if the system is utilizing a hardware
  1445. // compression cache. This ensures that only a safe amount
  1446. // of the compressed virtual cache is directly mapped so that
  1447. // if the hardware gets into trouble, we can bail it out.
  1448. //
  1449. MiEnsureAvailablePageOrWait (NULL, SystemCacheAddress);
  1450. //
  1451. // A wait operation occurred which could have changed the
  1452. // state of the PTE. Recheck the PTE state.
  1453. //
  1454. goto Recheck;
  1455. }
  1456. MiUnlinkPageFromList (Pfn1);
  1457. Pfn1->u3.e2.ReferenceCount += 1;
  1458. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1459. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  1460. MI_SET_MODIFIED (Pfn1, 1, 0x6);
  1461. ASSERT (Pfn1->u2.ShareCount == 0);
  1462. Pfn1->u2.ShareCount += 1;
  1463. ShareCountUpped = TRUE;
  1464. MI_SNAP_DATA (Pfn1, ProtoPte, 3);
  1465. MI_MAKE_VALID_PTE (TempPte,
  1466. PageFrameIndex,
  1467. Pfn1->OriginalPte.u.Soft.Protection,
  1468. NULL);
  1469. MI_SET_PTE_DIRTY (TempPte);
  1470. MI_WRITE_VALID_PTE (ProtoPte, TempPte);
  1471. //
  1472. // Increment the valid PTE count for the page containing
  1473. // the prototype PTE.
  1474. //
  1475. }
  1476. else {
  1477. //
  1478. // Page is not in memory, if a page of zeroes is requested,
  1479. // get a page of zeroes and make it valid.
  1480. //
  1481. if (MiEnsureAvailablePageOrWait (NULL, SystemCacheAddress)) {
  1482. //
  1483. // A wait operation occurred which could have changed the
  1484. // state of the PTE. Recheck the PTE state.
  1485. //
  1486. goto Recheck;
  1487. }
  1488. Event = MiGetInPageSupportBlock (TRUE, NULL);
  1489. if (Event == NULL) {
  1490. //
  1491. // A delay has already occurred if the allocation really failed
  1492. // so no need to do another here, just retry immediately.
  1493. //
  1494. goto Recheck;
  1495. }
  1496. //
  1497. // Increment the count of Pfn references for the control area
  1498. // corresponding to this file.
  1499. //
  1500. ControlArea = MiGetSubsectionAddress (ProtoPte)->ControlArea;
  1501. ControlArea->NumberOfPfnReferences += 1;
  1502. if (ControlArea->NumberOfUserReferences > 0) {
  1503. //
  1504. // There is a user reference to this file, always zero ahead.
  1505. //
  1506. DontZero = FALSE;
  1507. }
  1508. //
  1509. // Remove any page from the list and turn it into a transition
  1510. // page in the cache with read in progress set. This causes
  1511. // any other references to this page to block on the specified
  1512. // event while the copy operation to the cache is on-going.
  1513. //
  1514. PageFrameIndex = MiRemoveAnyPage(MI_GET_PAGE_COLOR_FROM_PTE (ProtoPte));
  1515. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1516. //
  1517. // Increment the valid PTE count for the page containing
  1518. // the prototype PTE.
  1519. //
  1520. MiInitializeTransitionPfn (PageFrameIndex, ProtoPte);
  1521. Pfn1->u2.ShareCount = 0;
  1522. Pfn1->u3.e2.ReferenceCount = 0; // for the add_locked_page macro
  1523. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, 24);
  1524. Pfn1->u3.e2.ReferenceCount = 1;
  1525. Pfn1->u3.e1.PrototypePte = 1;
  1526. MI_SET_MODIFIED (Pfn1, 1, 0x7);
  1527. Pfn1->u3.e1.ReadInProgress = 1;
  1528. Pfn1->u1.Event = &Event->Event;
  1529. Event->Pfn = Pfn1;
  1530. //
  1531. // This is needed in case a special kernel APC fires that ends up
  1532. // referencing the same page (this may even be through a different
  1533. // virtual address from the user/system one here).
  1534. //
  1535. Thread = PsGetCurrentThread ();
  1536. ASSERT (Thread->NestedFaultCount <= 1);
  1537. Thread->NestedFaultCount += 1;
  1538. TransitionState = TRUE;
  1539. MI_SNAP_DATA (Pfn1, ProtoPte, 4);
  1540. MI_MAKE_VALID_PTE (TempPte,
  1541. PageFrameIndex,
  1542. Pfn1->OriginalPte.u.Soft.Protection,
  1543. NULL);
  1544. MI_SET_PTE_DIRTY (TempPte);
  1545. //
  1546. // APCs must be explicitly disabled to prevent suspend APCs from
  1547. // interrupting this thread before the RtlCopyBytes completes.
  1548. // Otherwise this page can remain in transition indefinitely (until
  1549. // the suspend APC is released) which blocks any other threads that
  1550. // may reference it.
  1551. //
  1552. KeEnterCriticalRegionThread (&Thread->Tcb);
  1553. }
  1554. //
  1555. // Capture prefetch fault information.
  1556. //
  1557. if (CCPF_IS_PREFETCHER_ACTIVE()) {
  1558. TempPte2 = Pfn1->OriginalPte;
  1559. if (TempPte2.u.Soft.Prototype == 1) {
  1560. Subsection = MiGetSubsectionAddress (&TempPte2);
  1561. }
  1562. }
  1563. //
  1564. // Increment the share count of the page table page for this PTE.
  1565. //
  1566. PointerPde = MiGetPteAddress (PointerPte);
  1567. Pfn2 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
  1568. Pfn2->u2.ShareCount += 1;
  1569. MI_SET_GLOBAL_STATE (TempPte, 1);
  1570. #if defined (_WIN64)
  1571. if (MI_DETERMINE_OWNER (PointerPte) == 0) {
  1572. TempPte.u.Long &= ~MM_PTE_OWNER_MASK;
  1573. }
  1574. #else
  1575. TempPte.u.Hard.Owner = MI_DETERMINE_OWNER (PointerPte);
  1576. #endif
  1577. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  1578. AddToWorkingSet = TRUE;
  1579. UnlockAndCopy:
  1580. //
  1581. // Unlock the PFN database and perform the copy.
  1582. //
  1583. UNLOCK_PFN (OldIrql);
  1584. Copy:
  1585. Thread = PsGetCurrentThread ();
  1586. MmSavePageFaultReadAhead (Thread, &SavedState);
  1587. MmSetPageFaultReadAhead (Thread, 0);
  1588. status = STATUS_SUCCESS;
  1589. //
  1590. // Copy the user buffer into the cache under an exception handler.
  1591. //
  1592. try {
  1593. Buffer = (PVOID)((PCHAR)SystemCacheAddress + Offset);
  1594. RtlCopyBytes (Buffer, UserBuffer, CountInBytes);
  1595. if (TransitionState) {
  1596. //
  1597. // Only zero the memory outside the range if a page was taken
  1598. // from the free list.
  1599. //
  1600. if (Offset != 0) {
  1601. RtlZeroMemory (SystemCacheAddress, Offset);
  1602. }
  1603. if (DontZero == FALSE) {
  1604. EndFill = PAGE_SIZE - (Offset + CountInBytes);
  1605. if (EndFill != 0) {
  1606. Buffer = (PVOID)((PCHAR)Buffer + CountInBytes);
  1607. RtlZeroMemory (Buffer, EndFill);
  1608. }
  1609. }
  1610. }
  1611. } except (MiMapCacheExceptionFilter (&status, GetExceptionInformation())) {
  1612. if (status == STATUS_MULTIPLE_FAULT_VIOLATION) {
  1613. ASSERT (TransitionState == TRUE);
  1614. }
  1615. //
  1616. // Zero out the page if it came from the free list.
  1617. //
  1618. if (TransitionState) {
  1619. RtlZeroMemory (SystemCacheAddress, PAGE_SIZE);
  1620. }
  1621. }
  1622. MmResetPageFaultReadAhead (Thread, SavedState);
  1623. if (AddToWorkingSet) {
  1624. LOCK_PFN (OldIrql);
  1625. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  1626. ASSERT (Pfn1->PteAddress == ProtoPte);
  1627. if (TransitionState) {
  1628. KeLeaveCriticalRegionThread (&Thread->Tcb);
  1629. //
  1630. // This is a newly allocated page.
  1631. //
  1632. ASSERT (ShareCountUpped == FALSE);
  1633. ASSERT (Pfn1->u2.ShareCount <= 1);
  1634. ASSERT (Pfn1->u1.Event == &Event->Event);
  1635. MiMakeSystemAddressValidPfn (ProtoPte);
  1636. MI_SET_GLOBAL_STATE (TempPte, 0);
  1637. MI_WRITE_VALID_PTE (ProtoPte, TempPte);
  1638. Pfn1->u1.Event = (PVOID)Thread;
  1639. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  1640. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1641. ASSERT (Event->u1.e1.Completed == 0);
  1642. Event->u1.e1.Completed = 1;
  1643. ASSERT (Pfn1->u2.ShareCount == 0);
  1644. MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn1, 41);
  1645. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  1646. Pfn1->u3.e1.CacheAttribute = MiCached;
  1647. ASSERT (Pfn1->u3.e1.ReadInProgress == 1);
  1648. Pfn1->u3.e1.ReadInProgress = 0;
  1649. //
  1650. // Increment the share count since the page is
  1651. // being put into a working set.
  1652. //
  1653. Pfn1->u2.ShareCount += 1;
  1654. if (Event->WaitCount != 1) {
  1655. Event->IoStatus.Status = STATUS_SUCCESS;
  1656. Event->IoStatus.Information = 0;
  1657. KeSetEvent (&Event->Event, 0, FALSE);
  1658. }
  1659. if (DontZero != FALSE) {
  1660. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 40);
  1661. Pfn1->u3.e2.ReferenceCount += 1;
  1662. status = STATUS_CACHE_PAGE_LOCKED;
  1663. }
  1664. ASSERT (Thread->NestedFaultCount <= 3);
  1665. ASSERT (Thread->NestedFaultCount != 0);
  1666. Thread->NestedFaultCount -= 1;
  1667. if ((Thread->ApcNeeded == 1) && (Thread->NestedFaultCount == 0)) {
  1668. ApcNeeded = TRUE;
  1669. Thread->ApcNeeded = 0;
  1670. }
  1671. UNLOCK_PFN (OldIrql);
  1672. MiFreeInPageSupportBlock (Event);
  1673. }
  1674. else {
  1675. //
  1676. // This is either a frame that was originally on the transition list
  1677. // or was already valid when this routine began execution. Either
  1678. // way, the share count (and therefore the systemwide locked pages
  1679. // count) has been dealt with.
  1680. //
  1681. ASSERT (ShareCountUpped == TRUE);
  1682. if (Pfn1->u1.Event == NULL) {
  1683. Pfn1->u1.Event = (PVOID) Thread;
  1684. }
  1685. UNLOCK_PFN (OldIrql);
  1686. }
  1687. //
  1688. // Log prefetch fault information now that the PFN lock has been
  1689. // released and the PTE has been made valid. This minimizes PFN
  1690. // lock contention, allows CcPfLogPageFault to allocate (and fault on)
  1691. // pool, and allows other threads in this process to execute without
  1692. // faulting on this address.
  1693. //
  1694. if (Subsection != NULL) {
  1695. FileObject = Subsection->ControlArea->FilePointer;
  1696. FileOffset = MiStartingOffset (Subsection, ProtoPte);
  1697. Flags = 0;
  1698. ASSERT (Subsection->ControlArea->u.Flags.Image == 0);
  1699. if (Subsection->ControlArea->u.Flags.Rom) {
  1700. Flags |= CCPF_TYPE_ROM;
  1701. }
  1702. CcPfLogPageFault (FileObject, FileOffset, Flags);
  1703. }
  1704. LOCK_SYSTEM_WS (OldIrql, Thread);
  1705. WorkingSetIndex = MiLocateAndReserveWsle (&MmSystemCacheWs);
  1706. MiUpdateWsle (&WorkingSetIndex,
  1707. MiGetVirtualAddressMappedByPte (PointerPte),
  1708. MmSystemCacheWorkingSetList,
  1709. Pfn1);
  1710. MmSystemCacheWsle[WorkingSetIndex].u1.e1.SameProtectAsProto = 1;
  1711. MI_SET_PTE_IN_WORKING_SET (PointerPte, WorkingSetIndex);
  1712. UNLOCK_SYSTEM_WS (OldIrql);
  1713. if (ApcNeeded == TRUE) {
  1714. ASSERT (OldIrql < APC_LEVEL);
  1715. ASSERT (Thread->NestedFaultCount == 0);
  1716. ASSERT (Thread->ApcNeeded == 0);
  1717. KeRaiseIrql (APC_LEVEL, &OldIrql);
  1718. IoRetryIrpCompletions ();
  1719. KeLowerIrql (OldIrql);
  1720. }
  1721. }
  1722. return status;
  1723. }
  1724. LONG
  1725. MiMapCacheExceptionFilter (
  1726. IN PNTSTATUS Status,
  1727. IN PEXCEPTION_POINTERS ExceptionPointer
  1728. )
  1729. /*++
  1730. Routine Description:
  1731. This routine is a filter for exceptions during copying data
  1732. from the user buffer to the system cache. It stores the
  1733. status code from the exception record into the status argument.
  1734. In the case of an in page i/o error it returns the actual
  1735. error code and in the case of an access violation it returns
  1736. STATUS_INVALID_USER_BUFFER.
  1737. Arguments:
  1738. Status - Returns the status from the exception record.
  1739. ExceptionCode - Supplies the exception code to being checked.
  1740. Return Value:
  1741. ULONG - returns EXCEPTION_EXECUTE_HANDLER
  1742. --*/
  1743. {
  1744. NTSTATUS local;
  1745. local = ExceptionPointer->ExceptionRecord->ExceptionCode;
  1746. //
  1747. // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code
  1748. // from the exception record.
  1749. //
  1750. if (local == STATUS_IN_PAGE_ERROR) {
  1751. if (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) {
  1752. local = (NTSTATUS)ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
  1753. }
  1754. }
  1755. if (local == STATUS_ACCESS_VIOLATION) {
  1756. local = STATUS_INVALID_USER_BUFFER;
  1757. }
  1758. *Status = local;
  1759. return EXCEPTION_EXECUTE_HANDLER;
  1760. }
  1761. VOID
  1762. MmUnlockCachedPage (
  1763. IN PVOID AddressInCache
  1764. )
  1765. /*++
  1766. Routine Description:
  1767. This routine unlocks a previous locked cached page.
  1768. Arguments:
  1769. AddressInCache - Supplies the address where the page was locked
  1770. in the system cache. This must be the same
  1771. address that MmCopyToCachedPage was called with.
  1772. Return Value:
  1773. None.
  1774. --*/
  1775. {
  1776. PMMPTE PointerPte;
  1777. PMMPFN Pfn1;
  1778. KIRQL OldIrql;
  1779. PointerPte = MiGetPteAddress (AddressInCache);
  1780. ASSERT (PointerPte->u.Hard.Valid == 1);
  1781. Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);
  1782. LOCK_PFN (OldIrql);
  1783. if (Pfn1->u3.e2.ReferenceCount <= 1) {
  1784. KeBugCheckEx (MEMORY_MANAGEMENT,
  1785. 0x777,
  1786. (ULONG_PTR)PointerPte->u.Hard.PageFrameNumber,
  1787. Pfn1->u3.e2.ReferenceCount,
  1788. (ULONG_PTR)AddressInCache);
  1789. return;
  1790. }
  1791. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(Pfn1, 25);
  1792. UNLOCK_PFN (OldIrql);
  1793. return;
  1794. }