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.

729 lines
17 KiB

  1. /*++
  2. Copyright (c) 1990, 1991 Microsoft Corporation
  3. Module Name:
  4. wake.c
  5. Abstract:
  6. Author:
  7. Ken Reneris
  8. Environment:
  9. Kernel Mode
  10. Revision History:
  11. Steve Deng (sdeng) 20-Aug-2002
  12. Add support for PAE and Amd64. It is assumed that all the
  13. physical pages are below 4GB. Otherwise hibernation feature
  14. should be disabled.
  15. --*/
  16. #include "arccodes.h"
  17. #include "bootx86.h"
  18. extern PHARDWARE_PTE PDE;
  19. extern PHARDWARE_PTE HalPT;
  20. extern ULONG HiberNoMappings;
  21. extern BOOLEAN HiberIoError;
  22. extern ULONG HiberLastRemap;
  23. extern BOOLEAN HiberOutOfRemap;
  24. extern UCHAR BlpEnablePAEStart;
  25. extern UCHAR BlpEnablePAEEnd;
  26. extern UCHAR BlAmd64SwitchToLongModeStart;
  27. extern UCHAR BlAmd64SwitchToLongModeEnd;
  28. PVOID HiberTransVa;
  29. ULONG64 HiberTransVaAmd64;
  30. ULONG HiberCurrentMapIndex;
  31. #define PDE_SHIFT 22
  32. #define PTE_SHIFT 12
  33. #define PTE_INDEX_MASK 0x3ff
  34. #define PDPT_SHIFT_X86PAE 30
  35. #define PDE_SHIFT_X86PAE 21
  36. #define PTE_SHIFT_X86PAE 12
  37. #define PDE_INDEX_MASK_X86PAE 0x1ff
  38. #define PTE_INDEX_MASK_X86PAE 0x1ff
  39. VOID
  40. HiberSetupForWakeDispatchPAE (
  41. VOID
  42. );
  43. VOID
  44. HiberSetupForWakeDispatchX86 (
  45. VOID
  46. );
  47. VOID
  48. HiberSetupForWakeDispatchAmd64 (
  49. VOID
  50. );
  51. PVOID
  52. HbMapPte (
  53. IN ULONG PteToMap,
  54. IN ULONG Page
  55. )
  56. {
  57. PVOID Va;
  58. Va = (PVOID) (HiberVa + (PteToMap << PAGE_SHIFT));
  59. HbSetPte (Va, HiberPtes, PteToMap, Page);
  60. return Va;
  61. }
  62. PVOID
  63. HbNextSharedPage (
  64. IN ULONG PteToMap,
  65. IN ULONG RealPage
  66. )
  67. /*++
  68. Routine Description:
  69. Allocates the next available page in the free and
  70. maps the Hiber pte to the page. The allocated page
  71. is put onto the remap list
  72. Arguments:
  73. PteToMap - Which Hiber PTE to map
  74. RealPage - The page to enter into the remap table for
  75. this allocation
  76. Return Value:
  77. Virtual address of the mapping
  78. --*/
  79. {
  80. PULONG MapPage;
  81. PULONG RemapPage;
  82. ULONG DestPage;
  83. ULONG i;
  84. MapPage = (PULONG) (HiberVa + (PTE_MAP_PAGE << PAGE_SHIFT));
  85. RemapPage = (PULONG) (HiberVa + (PTE_REMAP_PAGE << PAGE_SHIFT));
  86. //
  87. // Loop until we find a free page which is not in
  88. // use by the loader image, then map it
  89. //
  90. while (HiberCurrentMapIndex < HiberNoMappings) {
  91. DestPage = MapPage[HiberCurrentMapIndex];
  92. HiberCurrentMapIndex += 1;
  93. i = HbPageDisposition (DestPage);
  94. if (i == HbPageInvalid) {
  95. HiberIoError = TRUE;
  96. return HiberBuffer;
  97. }
  98. if (i == HbPageNotInUse) {
  99. MapPage[HiberLastRemap] = DestPage;
  100. RemapPage[HiberLastRemap] = RealPage;
  101. HiberLastRemap += 1;
  102. HiberPageFrames[PteToMap] = DestPage;
  103. return HbMapPte(PteToMap, DestPage);
  104. }
  105. }
  106. HiberOutOfRemap = TRUE;
  107. return HiberBuffer;
  108. }
  109. VOID
  110. HbAllocatePtes (
  111. IN ULONG NumberPages,
  112. OUT PVOID *PteAddress,
  113. OUT PVOID *MappedAddress
  114. )
  115. /*++
  116. Routine Description:
  117. Allocated a consecutive chuck of Ptes.
  118. Arguments:
  119. NumberPage - Number of ptes to allocate
  120. PteAddress - Pointer to the first PTE
  121. MappedAddress - Base VA of the address mapped
  122. --*/
  123. {
  124. ULONG i;
  125. ULONG j;
  126. //
  127. // We use the HAL's PDE for mapping. Find enough free PTEs
  128. //
  129. for (i=0; i<=1024-NumberPages; i++) {
  130. for (j=0; j < NumberPages; j++) {
  131. if ((((PULONG)HalPT))[i+j]) {
  132. break;
  133. }
  134. }
  135. if (j == NumberPages) {
  136. *PteAddress = (PVOID) &HalPT[i];
  137. *MappedAddress = (PVOID) (0xffc00000 | (i<<12));
  138. return ;
  139. }
  140. i += j;
  141. }
  142. BlPrint("NoMem");
  143. while (1);
  144. }
  145. VOID
  146. HbSetPte (
  147. IN PVOID Va,
  148. IN PHARDWARE_PTE Pte,
  149. IN ULONG Index,
  150. IN ULONG PageNumber
  151. )
  152. /*++
  153. Routine Description:
  154. Sets the Pte to the corresponding page address
  155. Arguments:
  156. Va - the virtual address of the physical page described by PageNumber
  157. Pte - the base address of Page Table
  158. Index - the index into Page Table
  159. PageNumber - the page frame number of the pysical page to be mapped
  160. Return:
  161. None
  162. --*/
  163. {
  164. Pte[Index].PageFrameNumber = PageNumber;
  165. Pte[Index].Valid = 1;
  166. Pte[Index].Write = 1;
  167. Pte[Index].WriteThrough = 0;
  168. Pte[Index].CacheDisable = 0;
  169. _asm {
  170. mov eax, Va
  171. invlpg [eax]
  172. }
  173. }
  174. VOID
  175. HbMapPagePAE(
  176. PHARDWARE_PTE_X86PAE HbPdpt,
  177. PVOID VirtualAddress,
  178. ULONG PageFrameNumber
  179. )
  180. /*++
  181. Routine Description:
  182. This functions maps a virtural address into PAE page mapping structure.
  183. Arguments:
  184. HbPdpt - Supplies the base address of Page Directory Pointer Table.
  185. VirtualAddress - Supplies the virtual address to map
  186. PageFrameNumber - Supplies the physical page number to map the address to
  187. Return:
  188. None
  189. --*/
  190. {
  191. ULONG index;
  192. PHARDWARE_PTE_X86PAE HbPde, HbPte;
  193. //
  194. // If the PDPT entry is empty, allocate a new page for it.
  195. // Otherwise we simply map the page at this entry.
  196. //
  197. index = ((ULONG) VirtualAddress) >> PDPT_SHIFT_X86PAE;
  198. if(HbPdpt[index].Valid) {
  199. HbPde = HbMapPte (PTE_TRANSFER_PDE, (ULONG)(HbPdpt[index].PageFrameNumber));
  200. } else {
  201. HbPde = HbNextSharedPage(PTE_TRANSFER_PDE, 0);
  202. RtlZeroMemory (HbPde, PAGE_SIZE);
  203. HbPdpt[index].PageFrameNumber = HiberPageFrames[PTE_TRANSFER_PDE];
  204. HbPdpt[index].Valid = 1;
  205. }
  206. //
  207. // If the PDE entry is empty, allocate a new page for it.
  208. // Otherwise we simply map the page at this entry.
  209. //
  210. index = (((ULONG) VirtualAddress) >> PDE_SHIFT_X86PAE) & PDE_INDEX_MASK_X86PAE;
  211. if(HbPde[index].Valid) {
  212. HbPte = HbMapPte (PTE_WAKE_PTE, (ULONG)(HbPde[index].PageFrameNumber));
  213. } else {
  214. HbPte = HbNextSharedPage(PTE_WAKE_PTE, 0);
  215. RtlZeroMemory (HbPte, PAGE_SIZE);
  216. HbPde[index].PageFrameNumber = HiberPageFrames[PTE_WAKE_PTE];
  217. HbPde[index].Write = 1;
  218. HbPde[index].Valid = 1;
  219. }
  220. //
  221. // Locate the PTE of VirtualAddress and set it to PageFrameNumber
  222. //
  223. index = (((ULONG) VirtualAddress) >> PTE_SHIFT_X86PAE) & PDE_INDEX_MASK_X86PAE;
  224. HbPte[index].PageFrameNumber = PageFrameNumber;
  225. HbPte[index].Write = 1;
  226. HbPte[index].Valid = 1;
  227. }
  228. VOID
  229. HiberSetupForWakeDispatch (
  230. VOID
  231. )
  232. {
  233. if(BlAmd64UseLongMode) {
  234. //
  235. // if system was hibernated in long mode
  236. //
  237. HiberSetupForWakeDispatchAmd64();
  238. } else {
  239. if(BlUsePae) {
  240. //
  241. // if system was hibernated in pae mode
  242. //
  243. HiberSetupForWakeDispatchPAE();
  244. } else {
  245. //
  246. // if system was hibernated in 32-bit x86 mode
  247. //
  248. HiberSetupForWakeDispatchX86();
  249. }
  250. }
  251. }
  252. VOID
  253. HiberSetupForWakeDispatchX86 (
  254. VOID
  255. )
  256. {
  257. PHARDWARE_PTE HbPde;
  258. PHARDWARE_PTE HbPte;
  259. PHARDWARE_PTE WakePte;
  260. PHARDWARE_PTE TransVa;
  261. ULONG TransPde;
  262. ULONG WakePde;
  263. ULONG PteEntry;
  264. //
  265. // Allocate a transistion CR3. A page directory and table which
  266. // contains the hibernation PTEs
  267. //
  268. HbPde = HbNextSharedPage(PTE_TRANSFER_PDE, 0);
  269. HbPte = HbNextSharedPage(PTE_WAKE_PTE, 0); // TRANSFER_PTE, 0);
  270. RtlZeroMemory (HbPde, PAGE_SIZE);
  271. RtlZeroMemory (HbPte, PAGE_SIZE);
  272. //
  273. // Set PDE to point to PTE
  274. //
  275. TransPde = ((ULONG) HiberVa) >> PDE_SHIFT;
  276. HbPde[TransPde].PageFrameNumber = HiberPageFrames[PTE_WAKE_PTE];
  277. HbPde[TransPde].Write = 1;
  278. HbPde[TransPde].Valid = 1;
  279. //
  280. // Fill in the hiber PTEs
  281. //
  282. PteEntry = (((ULONG) HiberVa) >> PTE_SHIFT) & PTE_INDEX_MASK;
  283. TransVa = &HbPte[PteEntry];
  284. RtlCopyMemory (TransVa, HiberPtes, HIBER_PTES * sizeof(HARDWARE_PTE));
  285. //
  286. // Make another copy at the Va of the wake image hiber ptes
  287. //
  288. WakePte = HbPte;
  289. WakePde = ((ULONG) HiberIdentityVa) >> PDE_SHIFT;
  290. if (WakePde != TransPde) {
  291. WakePte = HbNextSharedPage(PTE_WAKE_PTE, 0);
  292. HbPde[WakePde].PageFrameNumber = HiberPageFrames[PTE_WAKE_PTE];
  293. HbPde[WakePde].Write = 1;
  294. HbPde[WakePde].Valid = 1;
  295. }
  296. PteEntry = (((ULONG) HiberIdentityVa) >> PTE_SHIFT) & PTE_INDEX_MASK;
  297. TransVa = &WakePte[PteEntry];
  298. RtlCopyMemory (TransVa, HiberPtes, HIBER_PTES * sizeof(HARDWARE_PTE));
  299. //
  300. // Set TransVa to be relative to the va of the transfer Cr3
  301. //
  302. HiberTransVa = (PVOID) (((PUCHAR) TransVa) - HiberVa + (PUCHAR) HiberIdentityVa);
  303. }
  304. VOID
  305. HiberSetupForWakeDispatchPAE (
  306. VOID
  307. )
  308. /*++
  309. Routine Description:
  310. Set up memory mappings for wake dispatch routine. This mapping will
  311. be used in a "transfer mode" in which the mapping of loader has
  312. already been discarded and the mapping of OS is not restored yet.
  313. For PAE systems, PAE is enabled before entering this "transfer mode".
  314. Arguments:
  315. None
  316. Return:
  317. None
  318. --*/
  319. {
  320. PHARDWARE_PTE_X86PAE HbPdpt;
  321. ULONG i, TransferCR3;
  322. //
  323. // Borrow the pte at PTE_SOURCE temporarily for Pdpt
  324. //
  325. HbPdpt = HbNextSharedPage(PTE_SOURCE, 0);
  326. RtlZeroMemory (HbPdpt, PAGE_SIZE);
  327. TransferCR3 = HiberPageFrames[PTE_SOURCE];
  328. //
  329. // Map in the pages where the code of BlpEnablePAE() resides.
  330. // This has to be a 1-1 mapping, i.e. the value of virtual
  331. // address is the same as the value of physical address
  332. //
  333. HbMapPagePAE( HbPdpt,
  334. (PVOID)(&BlpEnablePAEStart),
  335. (ULONG)(&BlpEnablePAEStart) >> PAGE_SHIFT);
  336. HbMapPagePAE( HbPdpt,
  337. (PVOID)(&BlpEnablePAEEnd),
  338. (ULONG)(&BlpEnablePAEEnd) >> PAGE_SHIFT);
  339. //
  340. // Map in the pages that is reserved for hibernation use
  341. //
  342. for (i = 0; i < HIBER_PTES; i++) {
  343. HbMapPagePAE( HbPdpt,
  344. (PUCHAR)HiberVa + PAGE_SIZE * i,
  345. (*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
  346. }
  347. for (i = 0; i < HIBER_PTES; i++) {
  348. HbMapPagePAE( HbPdpt,
  349. (PUCHAR)HiberIdentityVa + PAGE_SIZE * i,
  350. (*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
  351. }
  352. //
  353. // Update the value of HiberPageFrames[PTE_TRANSFER_PDE] to the
  354. // TransferCR3. The wake dispatch function will read this entry
  355. // for the CR3 value in transfer mode
  356. //
  357. HiberPageFrames[PTE_TRANSFER_PDE] = TransferCR3;
  358. //
  359. // HiberTransVa points to the PTEs reserved for hibernation code.
  360. // HiberTransVa is similar to HiberPtes but it is relative to
  361. // transfer Cr3
  362. //
  363. HiberTransVa = (PVOID)((PUCHAR) HiberIdentityVa +
  364. (PTE_WAKE_PTE << PAGE_SHIFT) +
  365. sizeof(HARDWARE_PTE_X86PAE) *
  366. (((ULONG_PTR)HiberIdentityVa >> PTE_SHIFT_X86PAE) & PTE_INDEX_MASK_X86PAE));
  367. }
  368. #define AMD64_MAPPING_LEVELS 4
  369. typedef struct _HB_AMD64_MAPPING_INFO {
  370. ULONG PteToUse;
  371. ULONG AddressMask;
  372. ULONG AddressShift;
  373. } CONST HB_AMD64_MAPPING_INFO, *PHB_AMD64_MAPPING_INFO;
  374. HB_AMD64_MAPPING_INFO HbAmd64MappingInfo[AMD64_MAPPING_LEVELS] =
  375. {
  376. { PTE_WAKE_PTE, 0x1ff, 12 },
  377. { PTE_TRANSFER_PDE, 0x1ff, 21 },
  378. { PTE_DEST, 0x1ff, 30 },
  379. { PTE_SOURCE, 0x1ff, 39 }
  380. };
  381. #define _HARDWARE_PTE_WORKING_SET_BITS 11
  382. typedef struct _HARDWARE_PTE_AMD64 {
  383. ULONG64 Valid : 1;
  384. ULONG64 Write : 1; // UP version
  385. ULONG64 Owner : 1;
  386. ULONG64 WriteThrough : 1;
  387. ULONG64 CacheDisable : 1;
  388. ULONG64 Accessed : 1;
  389. ULONG64 Dirty : 1;
  390. ULONG64 LargePage : 1;
  391. ULONG64 Global : 1;
  392. ULONG64 CopyOnWrite : 1; // software field
  393. ULONG64 Prototype : 1; // software field
  394. ULONG64 reserved0 : 1; // software field
  395. ULONG64 PageFrameNumber : 28;
  396. ULONG64 reserved1 : 24 - (_HARDWARE_PTE_WORKING_SET_BITS+1);
  397. ULONG64 SoftwareWsIndex : _HARDWARE_PTE_WORKING_SET_BITS;
  398. ULONG64 NoExecute : 1;
  399. } HARDWARE_PTE_AMD64, *PHARDWARE_PTE_AMD64;
  400. VOID
  401. HbMapPageAmd64(
  402. PHARDWARE_PTE_AMD64 RootLevelPageTable,
  403. ULONGLONG VirtualAddress,
  404. ULONG PageFrameNumber
  405. )
  406. /*++
  407. Routine Description:
  408. This functions maps a virtural address into Amd64 page mapping structure.
  409. Arguments:
  410. RootLevelPageTable - Supplies the base address of Page-Map Level-4 Table.
  411. VirtualAddress - Supplies the virtual address to map
  412. PageFrameNumber - Supplies the physical page number to be mapped
  413. Return:
  414. None
  415. --*/
  416. {
  417. LONG i;
  418. ULONG index, PteToUse;
  419. PHARDWARE_PTE_AMD64 PageTable, PageTableTmp;
  420. PageTable = RootLevelPageTable;
  421. //
  422. // Build a 4-level mapping top-down
  423. //
  424. for(i = AMD64_MAPPING_LEVELS - 1; i >= 0; i--) {
  425. index = (ULONG)((VirtualAddress >> HbAmd64MappingInfo[i].AddressShift) &
  426. HbAmd64MappingInfo[i].AddressMask);
  427. if (i > 0) {
  428. PteToUse = HbAmd64MappingInfo[i-1].PteToUse;
  429. if (PageTable[index].Valid) {
  430. //
  431. // If a page table entry is valid we simply map the page
  432. // at it and go to the next mapping level.
  433. //
  434. PageTable = HbMapPte(PteToUse,
  435. (ULONG)PageTable[index].PageFrameNumber);
  436. } else {
  437. //
  438. // If a page entry is invalid, we allocate a new page
  439. // from the free page list and reference the new page
  440. // from this page entry.
  441. //
  442. PageTableTmp = HbNextSharedPage(PteToUse, 0);
  443. PageTable[index].PageFrameNumber = HiberPageFrames[PteToUse];
  444. PageTable[index].Valid = 1;
  445. PageTable[index].Write = 1;
  446. RtlZeroMemory (PageTableTmp, PAGE_SIZE);
  447. PageTable = PageTableTmp;
  448. }
  449. } else {
  450. //
  451. // Now we come to the PTE level. Set this PTE entry to the
  452. // target page frame number.
  453. //
  454. PageTable[index].PageFrameNumber = PageFrameNumber;
  455. PageTable[index].Valid = 1;
  456. PageTable[index].Write = 1;
  457. }
  458. }
  459. }
  460. VOID
  461. HiberSetupForWakeDispatchAmd64(
  462. VOID
  463. )
  464. /*++
  465. Routine Description:
  466. This function prepares memory mapping for the transition period
  467. where neither loader's nor kernel's mapping is available. The
  468. processor runs in long mode in transition period.
  469. We'll create mapping for the following virtual addresses
  470. - the code handles long mode switch
  471. - loader's HiberVa
  472. - wake image's HiberVa
  473. Arguments:
  474. None
  475. Return:
  476. None
  477. --*/
  478. {
  479. PHARDWARE_PTE_AMD64 HbTopLevelPTE;
  480. ULONG i;
  481. ULONG TransferCR3;
  482. //
  483. // Borrow the pte at PTE_SOURCE for temporary use here
  484. //
  485. HbTopLevelPTE = HbNextSharedPage(PTE_SOURCE, 0);
  486. RtlZeroMemory (HbTopLevelPTE, PAGE_SIZE);
  487. TransferCR3 = HiberPageFrames[PTE_SOURCE];
  488. //
  489. // Map in the pages that is holding thee code of _BlAmd64SwitchToLongMode.
  490. // This has to be a 1-1 mapping, i.e. the virtual address equals physical
  491. // address
  492. //
  493. HbMapPageAmd64(
  494. HbTopLevelPTE,
  495. (ULONGLONG)(&BlAmd64SwitchToLongModeStart),
  496. (ULONG)(&BlAmd64SwitchToLongModeStart) >> PAGE_SHIFT);
  497. HbMapPageAmd64(
  498. HbTopLevelPTE,
  499. (ULONGLONG)(&BlAmd64SwitchToLongModeEnd),
  500. (ULONG)(&BlAmd64SwitchToLongModeEnd) >> PAGE_SHIFT);
  501. //
  502. // Map in the loader's HiberVa
  503. //
  504. for (i = 0; i < HIBER_PTES; i++) {
  505. HbMapPageAmd64(HbTopLevelPTE,
  506. (ULONGLONG)((ULONG_PTR)HiberVa + PAGE_SIZE * i),
  507. (*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
  508. }
  509. //
  510. // Map in the wake image's HiberVa. Note that the hiber pte at
  511. // PTE_WAKE_PTE will be set to the the right value as a result
  512. // of this mapping.
  513. //
  514. for (i = 0; i < HIBER_PTES; i++) {
  515. HbMapPageAmd64(HbTopLevelPTE,
  516. HiberIdentityVaAmd64 + PAGE_SIZE * i,
  517. (*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
  518. }
  519. //
  520. // Update the value of HiberPageFrames[PTE_TRANSFER_PDE] to the
  521. // TransferCR3. The wake dispatch function will read this entry
  522. // for the CR3 value in transition mode
  523. //
  524. HiberPageFrames[PTE_TRANSFER_PDE] = TransferCR3;
  525. //
  526. // HiberTransVaAmd64 points to the wake image's hiber ptes. It is
  527. // is relative to transfer Cr3
  528. //
  529. HiberTransVaAmd64 = HiberIdentityVaAmd64 +
  530. (PTE_WAKE_PTE << PAGE_SHIFT) +
  531. sizeof(HARDWARE_PTE_AMD64) * ((HiberIdentityVaAmd64 >> HbAmd64MappingInfo[0].AddressShift) & HbAmd64MappingInfo[0].AddressMask);
  532. }