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.

638 lines
16 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. paesup.c
  5. Abstract:
  6. This module contains the machine dependent support for the x86 PAE
  7. architecture.
  8. Author:
  9. Landy Wang (landyw) 15-Nov-1998
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. #if defined (_X86PAE_)
  14. #define PAES_PER_PAGE (PAGE_SIZE / sizeof(PAE_ENTRY))
  15. #define MINIMUM_PAE_SLIST_THRESHOLD (PAES_PER_PAGE * 1)
  16. #define MINIMUM_PAE_THRESHOLD (PAES_PER_PAGE * 4)
  17. #define REPLENISH_PAE_SIZE (PAES_PER_PAGE * 16)
  18. #define EXCESS_PAE_THRESHOLD (PAES_PER_PAGE * 20)
  19. #define MM_HIGHEST_PAE_PAGE 0xFFFFF
  20. ULONG MiFreePaeEntries;
  21. PAE_ENTRY MiFirstFreePae;
  22. LONG MmAllocatedPaePages;
  23. KSPIN_LOCK MiPaeLock;
  24. SLIST_HEADER MiPaeEntrySList;
  25. PAE_ENTRY MiSystemPaeVa;
  26. LONG
  27. MiPaeAllocatePages (
  28. VOID
  29. );
  30. VOID
  31. MiPaeFreePages (
  32. PVOID VirtualAddress
  33. );
  34. #pragma alloc_text(INIT,MiPaeInitialize)
  35. #pragma alloc_text(PAGE,MiPaeFreePages)
  36. VOID
  37. MiMarkMdlPageAttributes (
  38. IN PMDL Mdl,
  39. IN PFN_NUMBER NumberOfPages,
  40. IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
  41. );
  42. VOID
  43. MiPaeInitialize (
  44. VOID
  45. )
  46. {
  47. InitializeSListHead (&MiPaeEntrySList);
  48. KeInitializeSpinLock (&MiPaeLock);
  49. InitializeListHead (&MiFirstFreePae.PaeEntry.ListHead);
  50. }
  51. ULONG
  52. MiPaeAllocate (
  53. OUT PPAE_ENTRY *Va
  54. )
  55. /*++
  56. Routine Description:
  57. This routine allocates the top level page directory pointer structure.
  58. This structure will contain 4 PDPTEs.
  59. Arguments:
  60. Va - Supplies a place to put the virtual address this page can be accessed
  61. at.
  62. Return Value:
  63. Returns a virtual and physical address suitable for use as a top
  64. level page directory pointer page. The page returned must be below
  65. physical 4GB as required by the processor.
  66. Returns 0 if no page was allocated.
  67. Environment:
  68. Kernel mode. No locks may be held.
  69. --*/
  70. {
  71. LOGICAL FlushedOnce;
  72. PPAE_ENTRY Pae2;
  73. PPAE_ENTRY Pae3;
  74. PPAE_ENTRY Pae3Base;
  75. PPAE_ENTRY Pae;
  76. PPAE_ENTRY PaeBase;
  77. PFN_NUMBER PageFrameIndex;
  78. PSLIST_ENTRY SingleListEntry;
  79. ULONG j;
  80. ULONG Entries;
  81. KLOCK_QUEUE_HANDLE LockHandle;
  82. #if DBG
  83. PMMPFN Pfn1;
  84. #endif
  85. FlushedOnce = FALSE;
  86. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  87. do {
  88. //
  89. // Pop an entry from the freelist.
  90. //
  91. SingleListEntry = InterlockedPopEntrySList (&MiPaeEntrySList);
  92. if (SingleListEntry != NULL) {
  93. Pae = CONTAINING_RECORD (SingleListEntry,
  94. PAE_ENTRY,
  95. NextPae);
  96. PaeBase = (PPAE_ENTRY)PAGE_ALIGN(Pae);
  97. *Va = Pae;
  98. PageFrameIndex = PaeBase->PaeEntry.PageFrameNumber;
  99. ASSERT (PageFrameIndex <= MM_HIGHEST_PAE_PAGE);
  100. return (PageFrameIndex << PAGE_SHIFT) + BYTE_OFFSET (Pae);
  101. }
  102. KeAcquireInStackQueuedSpinLock (&MiPaeLock, &LockHandle);
  103. if (MiFreePaeEntries != 0) {
  104. ASSERT (IsListEmpty (&MiFirstFreePae.PaeEntry.ListHead) == 0);
  105. Pae = (PPAE_ENTRY) RemoveHeadList (&MiFirstFreePae.PaeEntry.ListHead);
  106. PaeBase = (PPAE_ENTRY)PAGE_ALIGN(Pae);
  107. PaeBase->PaeEntry.EntriesInUse += 1;
  108. #if DBG
  109. RtlZeroMemory ((PVOID)Pae, sizeof(PAE_ENTRY));
  110. Pfn1 = MI_PFN_ELEMENT (PaeBase->PaeEntry.PageFrameNumber);
  111. ASSERT (Pfn1->u2.ShareCount == 1);
  112. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  113. ASSERT (Pfn1->u3.e1.PageLocation == ActiveAndValid);
  114. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  115. #endif
  116. MiFreePaeEntries -= 1;
  117. //
  118. // Since we're holding the spinlock, dequeue a chain of entries
  119. // for the SLIST.
  120. //
  121. Entries = MiFreePaeEntries;
  122. if (Entries != 0) {
  123. if (Entries > MINIMUM_PAE_SLIST_THRESHOLD) {
  124. Entries = MINIMUM_PAE_SLIST_THRESHOLD;
  125. }
  126. ASSERT (IsListEmpty (&MiFirstFreePae.PaeEntry.ListHead) == 0);
  127. Pae2 = (PPAE_ENTRY) RemoveHeadList (&MiFirstFreePae.PaeEntry.ListHead);
  128. Pae2->NextPae.Next = NULL;
  129. Pae3 = Pae2;
  130. Pae3Base = (PPAE_ENTRY)PAGE_ALIGN(Pae3);
  131. Pae3Base->PaeEntry.EntriesInUse += 1;
  132. for (j = 1; j < Entries; j += 1) {
  133. ASSERT (IsListEmpty (&MiFirstFreePae.PaeEntry.ListHead) == 0);
  134. Pae3->NextPae.Next = (PSLIST_ENTRY) RemoveHeadList (&MiFirstFreePae.PaeEntry.ListHead);
  135. Pae3 = (PPAE_ENTRY) Pae3->NextPae.Next;
  136. Pae3Base = (PPAE_ENTRY)PAGE_ALIGN(Pae3);
  137. Pae3Base->PaeEntry.EntriesInUse += 1;
  138. }
  139. MiFreePaeEntries -= Entries;
  140. KeReleaseInStackQueuedSpinLock (&LockHandle);
  141. Pae3->NextPae.Next = NULL;
  142. InterlockedPushListSList (&MiPaeEntrySList,
  143. (PSLIST_ENTRY) Pae2,
  144. (PSLIST_ENTRY) Pae3,
  145. Entries);
  146. }
  147. else {
  148. KeReleaseInStackQueuedSpinLock (&LockHandle);
  149. }
  150. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  151. *Va = Pae;
  152. PageFrameIndex = PaeBase->PaeEntry.PageFrameNumber;
  153. ASSERT (PageFrameIndex <= MM_HIGHEST_PAE_PAGE);
  154. return (PageFrameIndex << PAGE_SHIFT) + BYTE_OFFSET (Pae);
  155. }
  156. KeReleaseInStackQueuedSpinLock (&LockHandle);
  157. if (FlushedOnce == TRUE) {
  158. break;
  159. }
  160. //
  161. // No free pages in the cachelist, replenish the list now.
  162. //
  163. if (MiPaeAllocatePages () == 0) {
  164. InterlockedIncrement (&MiDelayPageFaults);
  165. //
  166. // Attempt to move pages to the standby list.
  167. //
  168. MmEmptyAllWorkingSets ();
  169. MiFlushAllPages();
  170. KeDelayExecutionThread (KernelMode,
  171. FALSE,
  172. (PLARGE_INTEGER)&MmHalfSecond);
  173. InterlockedDecrement (&MiDelayPageFaults);
  174. FlushedOnce = TRUE;
  175. //
  176. // Since all the working sets have been trimmed, check whether
  177. // another thread has replenished our list. If not, then attempt
  178. // to do so since the working set pain has already been absorbed.
  179. //
  180. if (MiFreePaeEntries < MINIMUM_PAE_THRESHOLD) {
  181. MiPaeAllocatePages ();
  182. }
  183. }
  184. } while (TRUE);
  185. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  186. return 0;
  187. }
  188. VOID
  189. MiPaeFree (
  190. PPAE_ENTRY Pae
  191. )
  192. /*++
  193. Routine Description:
  194. This routine releases the top level page directory pointer page.
  195. Arguments:
  196. PageFrameIndex - Supplies the top level page directory pointer page.
  197. Return Value:
  198. None.
  199. Environment:
  200. Kernel mode. No locks may be held.
  201. --*/
  202. {
  203. ULONG i;
  204. PLIST_ENTRY NextEntry;
  205. PPAE_ENTRY PaeBase;
  206. KLOCK_QUEUE_HANDLE LockHandle;
  207. #if DBG
  208. PMMPTE PointerPte;
  209. PFN_NUMBER PageFrameIndex;
  210. PMMPFN Pfn1;
  211. PointerPte = MiGetPteAddress (Pae);
  212. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  213. //
  214. // This page must be in the first 4GB of RAM.
  215. //
  216. ASSERT (PageFrameIndex <= MM_HIGHEST_PAE_PAGE);
  217. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  218. ASSERT (Pfn1->u2.ShareCount == 1);
  219. ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  220. ASSERT (Pfn1->u3.e1.PageLocation == ActiveAndValid);
  221. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  222. #endif
  223. if (ExQueryDepthSList (&MiPaeEntrySList) < MINIMUM_PAE_SLIST_THRESHOLD) {
  224. InterlockedPushEntrySList (&MiPaeEntrySList, &Pae->NextPae);
  225. return;
  226. }
  227. PaeBase = (PPAE_ENTRY)PAGE_ALIGN(Pae);
  228. KeAcquireInStackQueuedSpinLock (&MiPaeLock, &LockHandle);
  229. PaeBase->PaeEntry.EntriesInUse -= 1;
  230. if ((PaeBase->PaeEntry.EntriesInUse == 0) &&
  231. (MiFreePaeEntries > EXCESS_PAE_THRESHOLD)) {
  232. //
  233. // Free the entire page.
  234. //
  235. i = 1;
  236. NextEntry = MiFirstFreePae.PaeEntry.ListHead.Flink;
  237. while (NextEntry != &MiFirstFreePae.PaeEntry.ListHead) {
  238. Pae = CONTAINING_RECORD (NextEntry,
  239. PAE_ENTRY,
  240. PaeEntry.ListHead);
  241. if (PAGE_ALIGN(Pae) == PaeBase) {
  242. RemoveEntryList (NextEntry);
  243. i += 1;
  244. }
  245. NextEntry = Pae->PaeEntry.ListHead.Flink;
  246. }
  247. ASSERT (i == PAES_PER_PAGE - 1);
  248. MiFreePaeEntries -= (PAES_PER_PAGE - 1);
  249. KeReleaseInStackQueuedSpinLock (&LockHandle);
  250. MiPaeFreePages (PaeBase);
  251. }
  252. else {
  253. InsertTailList (&MiFirstFreePae.PaeEntry.ListHead,
  254. &Pae->PaeEntry.ListHead);
  255. MiFreePaeEntries += 1;
  256. KeReleaseInStackQueuedSpinLock (&LockHandle);
  257. }
  258. return;
  259. }
  260. LONG
  261. MiPaeAllocatePages (
  262. VOID
  263. )
  264. /*++
  265. Routine Description:
  266. This routine replenishes the PAE top level mapping list.
  267. Arguments:
  268. None.
  269. Return Value:
  270. The number of pages allocated.
  271. Environment:
  272. Kernel mode, IRQL of APC_LEVEL or below.
  273. --*/
  274. {
  275. PMDL MemoryDescriptorList;
  276. LONG AllocatedPaePages;
  277. ULONG i;
  278. ULONG j;
  279. PPFN_NUMBER SlidePage;
  280. PPFN_NUMBER Page;
  281. PFN_NUMBER PageFrameIndex;
  282. ULONG_PTR ActualPages;
  283. PMMPTE PointerPte;
  284. PVOID BaseAddress;
  285. PPAE_ENTRY Pae;
  286. ULONG NumberOfPages;
  287. MMPTE TempPte;
  288. PHYSICAL_ADDRESS HighAddress;
  289. PHYSICAL_ADDRESS LowAddress;
  290. PHYSICAL_ADDRESS SkipBytes;
  291. KLOCK_QUEUE_HANDLE LockHandle;
  292. #if defined (_MI_MORE_THAN_4GB_)
  293. if (MiNoLowMemory != 0) {
  294. BaseAddress = MiAllocateLowMemory (PAGE_SIZE,
  295. 0,
  296. MiNoLowMemory - 1,
  297. 0,
  298. (PVOID)0x123,
  299. MmCached,
  300. 'DeaP');
  301. if (BaseAddress == NULL) {
  302. return 0;
  303. }
  304. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress(BaseAddress));
  305. Pae = (PPAE_ENTRY) BaseAddress;
  306. Pae->PaeEntry.EntriesInUse = 0;
  307. Pae->PaeEntry.PageFrameNumber = PageFrameIndex;
  308. Pae += 1;
  309. KeAcquireInStackQueuedSpinLock (&MiPaeLock, &LockHandle);
  310. for (i = 1; i < PAES_PER_PAGE; i += 1) {
  311. InsertTailList (&MiFirstFreePae.PaeEntry.ListHead,
  312. &Pae->PaeEntry.ListHead);
  313. Pae += 1;
  314. }
  315. MiFreePaeEntries += (PAES_PER_PAGE - 1);
  316. KeReleaseInStackQueuedSpinLock (&LockHandle);
  317. InterlockedIncrement (&MmAllocatedPaePages);
  318. return 1;
  319. }
  320. #endif
  321. NumberOfPages = REPLENISH_PAE_SIZE / PAES_PER_PAGE;
  322. AllocatedPaePages = 0;
  323. HighAddress.QuadPart = (ULONGLONG)_4gb - 1;
  324. LowAddress.QuadPart = 0;
  325. SkipBytes.QuadPart = 0;
  326. //
  327. // This is a potentially expensive call so pick up a chunk of pages
  328. // at once to amortize the cost.
  329. //
  330. MemoryDescriptorList = MmAllocatePagesForMdl (LowAddress,
  331. HighAddress,
  332. SkipBytes,
  333. NumberOfPages << PAGE_SHIFT);
  334. if (MemoryDescriptorList == NULL) {
  335. return 0;
  336. }
  337. ActualPages = MemoryDescriptorList->ByteCount >> PAGE_SHIFT;
  338. MiMarkMdlPageAttributes (MemoryDescriptorList, ActualPages, MiCached);
  339. TempPte = ValidKernelPte;
  340. Page = (PPFN_NUMBER)(MemoryDescriptorList + 1);
  341. //
  342. // Map each page individually as they may need to be freed individually
  343. // later.
  344. //
  345. for (i = 0; i < ActualPages; i += 1) {
  346. PageFrameIndex = *Page;
  347. PointerPte = MiReserveSystemPtes (1, SystemPteSpace);
  348. if (PointerPte == NULL) {
  349. //
  350. // Free any remaining pages in the MDL as they are not mapped.
  351. // Slide the MDL pages forward so the mapped ones are kept.
  352. //
  353. MmInitializeMdl (MemoryDescriptorList,
  354. 0,
  355. (ActualPages - i) << PAGE_SHIFT);
  356. SlidePage = (PPFN_NUMBER)(MemoryDescriptorList + 1);
  357. while (i < ActualPages) {
  358. i += 1;
  359. *SlidePage = *Page;
  360. SlidePage += 1;
  361. Page += 1;
  362. }
  363. MmFreePagesFromMdl (MemoryDescriptorList);
  364. break;
  365. }
  366. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  367. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  368. BaseAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  369. Pae = (PPAE_ENTRY) BaseAddress;
  370. Pae->PaeEntry.EntriesInUse = 0;
  371. Pae->PaeEntry.PageFrameNumber = PageFrameIndex;
  372. Pae += 1;
  373. //
  374. // Put the first chunk into the SLIST if it's still low, and just
  375. // enqueue all the other entries normally.
  376. //
  377. if ((i == 0) &&
  378. (ExQueryDepthSList (&MiPaeEntrySList) < MINIMUM_PAE_SLIST_THRESHOLD)) {
  379. (Pae - 1)->PaeEntry.EntriesInUse = PAES_PER_PAGE - 1;
  380. for (j = 1; j < PAES_PER_PAGE - 1; j += 1) {
  381. Pae->NextPae.Next = (PSLIST_ENTRY) (Pae + 1);
  382. Pae += 1;
  383. }
  384. Pae->NextPae.Next = NULL;
  385. InterlockedPushListSList (&MiPaeEntrySList,
  386. (PSLIST_ENTRY)((PPAE_ENTRY) BaseAddress + 1),
  387. (PSLIST_ENTRY) Pae,
  388. PAES_PER_PAGE - 1);
  389. }
  390. else {
  391. KeAcquireInStackQueuedSpinLock (&MiPaeLock, &LockHandle);
  392. for (j = 1; j < PAES_PER_PAGE; j += 1) {
  393. InsertTailList (&MiFirstFreePae.PaeEntry.ListHead,
  394. &Pae->PaeEntry.ListHead);
  395. Pae += 1;
  396. }
  397. MiFreePaeEntries += (PAES_PER_PAGE - 1);
  398. KeReleaseInStackQueuedSpinLock (&LockHandle);
  399. }
  400. AllocatedPaePages += 1;
  401. Page += 1;
  402. }
  403. ExFreePool (MemoryDescriptorList);
  404. InterlockedExchangeAdd (&MmAllocatedPaePages, AllocatedPaePages);
  405. return AllocatedPaePages;
  406. }
  407. VOID
  408. MiPaeFreePages (
  409. PVOID VirtualAddress
  410. )
  411. /*++
  412. Routine Description:
  413. This routine releases a single page that previously contained top level
  414. page directory pointer pages.
  415. Arguments:
  416. VirtualAddress - Supplies the virtual address of the page that contained
  417. top level page directory pointer pages.
  418. Return Value:
  419. None.
  420. Environment:
  421. Kernel mode. No locks held.
  422. --*/
  423. {
  424. ULONG MdlPages;
  425. PFN_NUMBER PageFrameIndex;
  426. PMMPTE PointerPte;
  427. PFN_NUMBER MdlHack[(sizeof(MDL) / sizeof(PFN_NUMBER)) + 1];
  428. PPFN_NUMBER MdlPage;
  429. PMDL MemoryDescriptorList;
  430. #if defined (_MI_MORE_THAN_4GB_)
  431. if (MiNoLowMemory != 0) {
  432. if (MiFreeLowMemory (VirtualAddress, 'DeaP') == TRUE) {
  433. InterlockedDecrement (&MmAllocatedPaePages);
  434. return;
  435. }
  436. }
  437. #endif
  438. MemoryDescriptorList = (PMDL)&MdlHack[0];
  439. MdlPages = 1;
  440. MmInitializeMdl (MemoryDescriptorList, 0, MdlPages << PAGE_SHIFT);
  441. MdlPage = (PPFN_NUMBER)(MemoryDescriptorList + 1);
  442. PointerPte = MiGetPteAddress (VirtualAddress);
  443. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  444. *MdlPage = PageFrameIndex;
  445. ASSERT ((MI_PFN_ELEMENT(PageFrameIndex))->u3.e1.CacheAttribute == MiCached);
  446. MiReleaseSystemPtes (PointerPte, 1, SystemPteSpace);
  447. MmFreePagesFromMdl (MemoryDescriptorList);
  448. InterlockedDecrement (&MmAllocatedPaePages);
  449. }
  450. #endif