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.

553 lines
12 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. hypermap.c
  5. Abstract:
  6. This module contains the routines which map physical pages into
  7. reserved PTEs within hyper space.
  8. Author:
  9. Lou Perazzoli (loup) 5-Apr-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. PMMPTE MiFirstReservedZeroingPte;
  15. KEVENT MiImageMappingPteEvent;
  16. #pragma alloc_text(PAGE,MiMapImageHeaderInHyperSpace)
  17. #pragma alloc_text(PAGE,MiUnmapImageHeaderInHyperSpace)
  18. PVOID
  19. MiMapPageInHyperSpace (
  20. IN PEPROCESS Process,
  21. IN PFN_NUMBER PageFrameIndex,
  22. IN PKIRQL OldIrql
  23. )
  24. /*++
  25. Routine Description:
  26. This procedure maps the specified physical page into hyper space
  27. and returns the virtual address which maps the page.
  28. ************************************
  29. * *
  30. * Returns with a spin lock held!!! *
  31. * *
  32. ************************************
  33. Arguments:
  34. Process - Supplies the current process.
  35. PageFrameIndex - Supplies the physical page number to map.
  36. OldIrql - Supplies a pointer in which to return the entry IRQL.
  37. Return Value:
  38. Returns the address where the requested page was mapped.
  39. RETURNS WITH THE HYPERSPACE SPIN LOCK HELD!!!!
  40. The routine MiUnmapHyperSpaceMap MUST be called to release the lock!!!!
  41. Environment:
  42. Kernel mode.
  43. --*/
  44. {
  45. MMPTE TempPte;
  46. PMMPTE PointerPte;
  47. PFN_NUMBER offset;
  48. ASSERT (PageFrameIndex != 0);
  49. PointerPte = MmFirstReservedMappingPte;
  50. UNREFERENCED_PARAMETER (Process); // Workaround compiler W4 bug.
  51. LOCK_HYPERSPACE (Process, OldIrql);
  52. //
  53. // Get offset to first free PTE.
  54. //
  55. offset = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  56. if (offset == 0) {
  57. //
  58. // All the reserved PTEs have been used, make them all invalid.
  59. //
  60. MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
  61. #if DBG
  62. {
  63. PMMPTE LastPte;
  64. LastPte = PointerPte + NUMBER_OF_MAPPING_PTES;
  65. do {
  66. ASSERT (LastPte->u.Long == 0);
  67. LastPte -= 1;
  68. } while (LastPte > PointerPte);
  69. }
  70. #endif
  71. //
  72. // Use the page frame number field of the first PTE as an
  73. // offset into the available mapping PTEs.
  74. //
  75. offset = NUMBER_OF_MAPPING_PTES;
  76. //
  77. // Flush entire TB only on processors executing this process.
  78. //
  79. KeFlushEntireTb (TRUE, FALSE);
  80. }
  81. //
  82. // Change offset for next time through.
  83. //
  84. PointerPte->u.Hard.PageFrameNumber = offset - 1;
  85. //
  86. // Point to free entry and make it valid.
  87. //
  88. PointerPte += offset;
  89. ASSERT (PointerPte->u.Hard.Valid == 0);
  90. TempPte = ValidPtePte;
  91. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  92. *PointerPte = TempPte;
  93. //
  94. // Return the VA that maps the page.
  95. //
  96. return MiGetVirtualAddressMappedByPte (PointerPte);
  97. }
  98. PVOID
  99. MiMapPageInHyperSpaceAtDpc (
  100. IN PEPROCESS Process,
  101. IN PFN_NUMBER PageFrameIndex
  102. )
  103. /*++
  104. Routine Description:
  105. This procedure maps the specified physical page into hyper space
  106. and returns the virtual address which maps the page.
  107. ************************************
  108. * *
  109. * Returns with a spin lock held!!! *
  110. * *
  111. ************************************
  112. Arguments:
  113. Process - Supplies the current process.
  114. PageFrameIndex - Supplies the physical page number to map.
  115. Return Value:
  116. Returns the address where the requested page was mapped.
  117. RETURNS WITH THE HYPERSPACE SPIN LOCK HELD!!!!
  118. The routine MiUnmapHyperSpaceMap MUST be called to release the lock!!!!
  119. Environment:
  120. Kernel mode, DISPATCH_LEVEL on entry.
  121. --*/
  122. {
  123. MMPTE TempPte;
  124. PMMPTE PointerPte;
  125. PFN_NUMBER offset;
  126. UNREFERENCED_PARAMETER (Process); // Workaround compiler W4 bug.
  127. ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
  128. ASSERT (PageFrameIndex != 0);
  129. LOCK_HYPERSPACE_AT_DPC (Process);
  130. //
  131. // Get offset to first free PTE.
  132. //
  133. PointerPte = MmFirstReservedMappingPte;
  134. offset = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  135. if (offset == 0) {
  136. //
  137. // All the reserved PTEs have been used, make them all invalid.
  138. //
  139. MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
  140. #if DBG
  141. {
  142. PMMPTE LastPte;
  143. LastPte = PointerPte + NUMBER_OF_MAPPING_PTES;
  144. do {
  145. ASSERT (LastPte->u.Long == 0);
  146. LastPte -= 1;
  147. } while (LastPte > PointerPte);
  148. }
  149. #endif
  150. //
  151. // Use the page frame number field of the first PTE as an
  152. // offset into the available mapping PTEs.
  153. //
  154. offset = NUMBER_OF_MAPPING_PTES;
  155. //
  156. // Flush entire TB only on processors executing this process.
  157. //
  158. KeFlushEntireTb (TRUE, FALSE);
  159. }
  160. //
  161. // Change offset for next time through.
  162. //
  163. PointerPte->u.Hard.PageFrameNumber = offset - 1;
  164. //
  165. // Point to free entry and make it valid.
  166. //
  167. PointerPte += offset;
  168. ASSERT (PointerPte->u.Hard.Valid == 0);
  169. TempPte = ValidPtePte;
  170. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  171. *PointerPte = TempPte;
  172. //
  173. // Return the VA that maps the page.
  174. //
  175. return MiGetVirtualAddressMappedByPte (PointerPte);
  176. }
  177. PVOID
  178. MiMapImageHeaderInHyperSpace (
  179. IN PFN_NUMBER PageFrameIndex
  180. )
  181. /*++
  182. Routine Description:
  183. This procedure maps the specified physical page into the
  184. PTE within hyper space reserved explicitly for image page
  185. header mapping. By reserving an explicit PTE for mapping
  186. the PTE, page faults can occur while the PTE is mapped within
  187. hyperspace and no other hyperspace maps will affect this PTE.
  188. Note that if another thread attempts to map an image at the
  189. same time, it will be forced into a wait state until the
  190. header is "unmapped".
  191. Arguments:
  192. PageFrameIndex - Supplies the physical page number to map.
  193. Return Value:
  194. Returns the virtual address where the specified physical page was
  195. mapped.
  196. Environment:
  197. Kernel mode.
  198. --*/
  199. {
  200. MMPTE TempPte;
  201. MMPTE OriginalPte;
  202. PMMPTE PointerPte;
  203. PVOID FlushVaPointer;
  204. ASSERT (PageFrameIndex != 0);
  205. TempPte = ValidPtePte;
  206. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  207. FlushVaPointer = (PVOID) IMAGE_MAPPING_PTE;
  208. //
  209. // Ensure both modified and accessed bits are set so the hardware doesn't
  210. // ever write this PTE.
  211. //
  212. ASSERT (TempPte.u.Hard.Dirty == 1);
  213. ASSERT (TempPte.u.Hard.Accessed == 1);
  214. PointerPte = MiGetPteAddress (IMAGE_MAPPING_PTE);
  215. do {
  216. OriginalPte.u.Long = 0;
  217. OriginalPte.u.Long = InterlockedCompareExchangePte (
  218. PointerPte,
  219. TempPte.u.Long,
  220. OriginalPte.u.Long);
  221. if (OriginalPte.u.Long == 0) {
  222. break;
  223. }
  224. //
  225. // Another thread modified the PTE just before us or the PTE was
  226. // already in use. This should be very rare - go the long way.
  227. //
  228. InterlockedIncrement ((PLONG)&MmWorkingSetList->NumberOfImageWaiters);
  229. //
  230. // Deliberately wait with a timeout since the PTE release runs
  231. // without lock synchronization so there is the extremely rare
  232. // race window which the timeout saves us from.
  233. //
  234. KeWaitForSingleObject (&MiImageMappingPteEvent,
  235. Executive,
  236. KernelMode,
  237. FALSE,
  238. (PLARGE_INTEGER)&MmOneSecond);
  239. InterlockedDecrement ((PLONG)&MmWorkingSetList->NumberOfImageWaiters);
  240. } while (TRUE);
  241. //
  242. // Use KeFlushMultiple even though only one PTE is being flushed
  243. // because this interface provides a way to just flush the
  244. // specified TB entry without writing a PTE and we always want
  245. // to do interlocked writes to this PTE.
  246. //
  247. // Note the flush must be made across all processors as this thread
  248. // may migrate. Also this must be done here instead of in the unmap
  249. // in order to support lock-free operation.
  250. //
  251. KeFlushMultipleTb (1,
  252. &FlushVaPointer,
  253. TRUE,
  254. TRUE,
  255. NULL,
  256. TempPte.u.Flush);
  257. return (PVOID) MiGetVirtualAddressMappedByPte (PointerPte);
  258. }
  259. VOID
  260. MiUnmapImageHeaderInHyperSpace (
  261. VOID
  262. )
  263. /*++
  264. Routine Description:
  265. This procedure unmaps the PTE reserved for mapping the image
  266. header, flushes the TB, and, if the WaitingForImageMapping field
  267. is not NULL, sets the specified event.
  268. Arguments:
  269. None.
  270. Return Value:
  271. None.
  272. Environment:
  273. Kernel mode.
  274. --*/
  275. {
  276. PMMPTE PointerPte;
  277. PointerPte = MiGetPteAddress (IMAGE_MAPPING_PTE);
  278. //
  279. // Capture the number of waiters.
  280. //
  281. ASSERT (PointerPte->u.Long != 0);
  282. #if defined (_WIN64)
  283. InterlockedExchange64 ((PLONG64)PointerPte, ZeroPte.u.Long);
  284. #elif defined(_X86PAE_)
  285. KeInterlockedSwapPte ((PHARDWARE_PTE)PointerPte,
  286. (PHARDWARE_PTE)&ZeroPte.u.Long);
  287. #else
  288. InterlockedExchange ((PLONG)PointerPte, ZeroPte.u.Long);
  289. #endif
  290. if (MmWorkingSetList->NumberOfImageWaiters != 0) {
  291. //
  292. // If there are any threads waiting, wake them all now. Note this
  293. // will wake threads in other processes as well, but it is very
  294. // rare that there are any waiters in the entire system period.
  295. //
  296. KePulseEvent (&MiImageMappingPteEvent, 0, FALSE);
  297. }
  298. return;
  299. }
  300. PVOID
  301. MiMapPageToZeroInHyperSpace (
  302. IN PFN_NUMBER PageFrameIndex
  303. )
  304. /*++
  305. Routine Description:
  306. This procedure maps the specified physical page into hyper space
  307. and returns the virtual address which maps the page.
  308. This is only to be used by THE zeroing page thread.
  309. Arguments:
  310. PageFrameIndex - Supplies the physical page number to map.
  311. Return Value:
  312. Returns the virtual address where the specified physical page was
  313. mapped.
  314. Environment:
  315. PASSIVE_LEVEL.
  316. --*/
  317. {
  318. PFN_NUMBER offset;
  319. MMPTE TempPte;
  320. PMMPTE PointerPte;
  321. ASSERT (PageFrameIndex != 0);
  322. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  323. PointerPte = MiFirstReservedZeroingPte;
  324. //
  325. // Get offset to first free PTE.
  326. //
  327. offset = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  328. if (offset == 0) {
  329. //
  330. // All the reserved PTEs have been used, make them all invalid.
  331. //
  332. MI_MAKING_MULTIPLE_PTES_INVALID (FALSE);
  333. #if DBG
  334. {
  335. PMMPTE LastPte;
  336. LastPte = PointerPte + NUMBER_OF_ZEROING_PTES;
  337. do {
  338. ASSERT (LastPte->u.Long == 0);
  339. LastPte -= 1;
  340. } while (LastPte > PointerPte);
  341. }
  342. #endif
  343. //
  344. // Use the page frame number field of the first PTE as an
  345. // offset into the available zeroing PTEs.
  346. //
  347. offset = NUMBER_OF_ZEROING_PTES;
  348. PointerPte->u.Hard.PageFrameNumber = offset;
  349. //
  350. // Flush entire TB only on processors executing this process as this
  351. // thread may migrate there at any time.
  352. //
  353. KeFlushEntireTb (TRUE, FALSE);
  354. }
  355. //
  356. // Change offset for next time through.
  357. //
  358. PointerPte->u.Hard.PageFrameNumber = offset - 1;
  359. //
  360. // Point to free entry and make it valid.
  361. //
  362. PointerPte += offset;
  363. ASSERT (PointerPte->u.Hard.Valid == 0);
  364. TempPte = ValidPtePte;
  365. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  366. *PointerPte = TempPte;
  367. //
  368. // Return the VA that maps the page.
  369. //
  370. return MiGetVirtualAddressMappedByPte (PointerPte);
  371. }