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.

4885 lines
138 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. hiber.c
  5. Abstract:
  6. Author:
  7. Ken Reneris (kenr) 13-April-1997
  8. Revision History:
  9. Elliot Shmukler (t-ellios) 8/7/1998 Added Hiber file compression
  10. Andrew Kadatch (akadatch)
  11. Added Xpress file compression
  12. Added DMA-based IO
  13. --*/
  14. #include "pop.h"
  15. #include "stdio.h" // for sprintf
  16. #include "inbv.h"
  17. #include "xpress.h" // XPRESS declarations
  18. // size of buffer to store compressed data
  19. #define POP_COMPRESSED_PAGE_SET_SIZE (((XPRESS_MAX_SIZE + 2 * XPRESS_HEADER_SIZE + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1)
  20. // Structure used to allocate memory for hand-crafted MDL
  21. typedef struct _DUMP_MDL {
  22. MDL BaseMdl;
  23. PFN_NUMBER PfnArray[POP_MAX_MDL_SIZE + 1];
  24. } DUMP_MDL[1];
  25. typedef struct _COMPRESSION_BLOCK {
  26. UCHAR Buffer[XPRESS_MAX_SIZE], *Ptr;
  27. } COMPRESSION_BLOCK, *PCOMPRESSION_BLOCK;
  28. // Data structures for DMA-based IO
  29. typedef struct
  30. {
  31. PUCHAR Beg; // ptr to the beginning of entire
  32. PUCHAR End; // ptr to the end of memory block
  33. PUCHAR Ptr; // ptr to beginning of region
  34. LONG Size; // size of region after ptr
  35. LONG SizeOvl; // size of overlapping piece starting from beginning of buffer
  36. } IOREGION;
  37. #define IOREGION_BUFF_PAGES 64 /* 256 KB */
  38. #define IOREGION_BUFF_SIZE (IOREGION_BUFF_PAGES << PAGE_SHIFT)
  39. typedef struct {
  40. PLARGE_INTEGER FirstMcb;
  41. PLARGE_INTEGER Mcb;
  42. ULONGLONG Base;
  43. } POP_MCB_CONTEXT, *PPOP_MCB_CONTEXT;
  44. #define HIBER_WRITE_PAGES_LOCALS_LIST(X)\
  45. X (ULONGLONG, FileBase); \
  46. X (ULONGLONG, PhysBase); \
  47. X (ULONG_PTR, Length); \
  48. X (ULONGLONG, McbOffset); \
  49. X (LARGE_INTEGER, IoLocation); \
  50. X (PHYSICAL_ADDRESS, pa); \
  51. X (PPOP_MCB_CONTEXT, CMcb); \
  52. X (PVOID, PageVa); \
  53. X (PMDL, Mdl); \
  54. X (PPFN_NUMBER, MdlPage); \
  55. X (PFN_NUMBER, NoPages); \
  56. X (PFN_NUMBER, FilePage); \
  57. X (ULONG, IoLength); \
  58. X (ULONG, i); \
  59. X (NTSTATUS, Status);
  60. typedef struct
  61. {
  62. DUMP_MDL DumpMdl;
  63. #define X(type,name) type name
  64. HIBER_WRITE_PAGES_LOCALS_LIST (X)
  65. #undef X
  66. } HIBER_WRITE_PAGES_LOCALS;
  67. typedef struct {
  68. IOREGION Free, Used, Busy;
  69. PFN_NUMBER FilePage[IOREGION_BUFF_PAGES];
  70. PVOID DumpLocalData;
  71. ULONG UseDma;
  72. ULONG DmaInitialized;
  73. struct {
  74. PUCHAR Ptr;
  75. ULONG Bytes;
  76. } Chk;
  77. HIBER_WRITE_PAGES_LOCALS HiberWritePagesLocals;
  78. } DMA_IOREGIONS;
  79. #define DmaIoPtr ((DMA_IOREGIONS *)(HiberContext->DmaIO))
  80. // May we use DMA IO?
  81. #define HIBER_USE_DMA(HiberContext) \
  82. (DmaIoPtr != NULL && \
  83. DmaIoPtr->UseDma && \
  84. HiberContext->DumpStack->Init.WritePendingRoutine != NULL)
  85. #define HbCopy(_hibercontext_,_dest_,_src_,_len_) { \
  86. ULONGLONG _starttime_; \
  87. \
  88. (_hibercontext_)->PerfInfo.BytesCopied += (ULONG)(_len_); \
  89. _starttime_ = HIBER_GET_TICK_COUNT(NULL); \
  90. RtlCopyMemory((_dest_),(_src_),(_len_)); \
  91. (_hibercontext_)->PerfInfo.CopyTicks += \
  92. HIBER_GET_TICK_COUNT(NULL) - _starttime_; \
  93. }
  94. #ifdef HIBER_DEBUG
  95. #define DBGOUT(x) DbgPrint x
  96. #else
  97. #define DBGOUT(x)
  98. #endif
  99. //
  100. // The performance counter on x86 doesn't work very well during hibernate
  101. // because interrupts are turned off and we don't get the rollovers. So use
  102. // RDTSC instead.
  103. //
  104. #if !defined(i386)
  105. #define HIBER_GET_TICK_COUNT(_x_) KeQueryPerformanceCounter(_x_).QuadPart
  106. #else
  107. __inline
  108. LONGLONG
  109. HIBER_GET_TICK_COUNT(
  110. OUT PLARGE_INTEGER Frequency OPTIONAL
  111. )
  112. {
  113. if (ARGUMENT_PRESENT(Frequency)) {
  114. Frequency->QuadPart = (ULONGLONG)KeGetCurrentPrcb()->MHz * 1000000;
  115. }
  116. _asm _emit 0x0f
  117. _asm _emit 0x31
  118. }
  119. #endif
  120. extern LARGE_INTEGER KdTimerDifference;
  121. extern UNICODE_STRING IoArcBootDeviceName;
  122. extern PUCHAR IoLoaderArcBootDeviceName;
  123. extern UNICODE_STRING IoArcHalDeviceName;
  124. extern POBJECT_TYPE IoFileObjectType;
  125. extern ULONG MmAvailablePages;
  126. extern PFN_NUMBER MmHighestPhysicalPage;
  127. extern ULONG MmHiberPages;
  128. extern ULONG MmZeroPageFile;
  129. KPROCESSOR_STATE PoWakeState;
  130. //
  131. // Define the size of the I/Os used to zero the hiber file
  132. //
  133. #define POP_ZERO_CHUNK_SIZE (64 * 1024)
  134. VOID
  135. RtlpGetStackLimits (
  136. OUT PULONG_PTR LowLimit,
  137. OUT PULONG_PTR HighLimit
  138. );
  139. NTSTATUS
  140. PopCreateHiberFile (
  141. IN PPOP_HIBER_FILE HiberFile,
  142. IN PWCHAR NameString,
  143. IN PLARGE_INTEGER FileSize,
  144. IN BOOLEAN DebugHiberFile
  145. );
  146. NTSTATUS
  147. PopCreateHiberLinkFile (
  148. IN PPOP_HIBER_CONTEXT HiberContext
  149. );
  150. VOID
  151. PopClearHiberFileSignature (
  152. IN BOOLEAN GetStats
  153. );
  154. VOID
  155. PopPreserveRange(
  156. IN PPOP_HIBER_CONTEXT HiberContext,
  157. IN PFN_NUMBER StartPage,
  158. IN PFN_NUMBER PageCount,
  159. IN ULONG Tag
  160. );
  161. VOID
  162. PopCloneRange(
  163. IN PPOP_HIBER_CONTEXT HiberContext,
  164. IN PFN_NUMBER StartPage,
  165. IN PFN_NUMBER PageCount,
  166. IN ULONG Tag
  167. );
  168. VOID
  169. PopDiscardRange(
  170. IN PPOP_HIBER_CONTEXT HiberContext,
  171. IN PFN_NUMBER StartPage,
  172. IN PFN_NUMBER PageCount,
  173. IN ULONG Tag
  174. );
  175. VOID
  176. PopSetRange (
  177. IN PPOP_HIBER_CONTEXT HiberContext,
  178. IN ULONG Flags,
  179. IN PFN_NUMBER StartPage,
  180. IN PFN_NUMBER PageCount,
  181. IN ULONG Tag
  182. );
  183. ULONG
  184. PopSimpleRangeCheck (
  185. IN PPOP_MEMORY_RANGE Range
  186. );
  187. VOID
  188. PopCreateDumpMdl (
  189. IN PMDL Mdl,
  190. IN PFN_NUMBER StartPage,
  191. IN PFN_NUMBER EndPage
  192. );
  193. PVOID
  194. PopAllocatePages (
  195. IN PPOP_HIBER_CONTEXT HiberContext,
  196. IN PFN_NUMBER NoPages
  197. );
  198. VOID
  199. PopWriteHiberPages (
  200. IN PPOP_HIBER_CONTEXT HiberContext,
  201. IN PVOID Page,
  202. IN PFN_NUMBER NoPages,
  203. IN PFN_NUMBER FilePage,
  204. IN HIBER_WRITE_PAGES_LOCALS *Locals
  205. );
  206. NTSTATUS
  207. PopWriteHiberImage (
  208. IN PPOP_HIBER_CONTEXT HiberContext,
  209. IN PPO_MEMORY_IMAGE MemImage,
  210. IN PPOP_HIBER_FILE HiberFile
  211. );
  212. VOID
  213. PopUpdateHiberComplete (
  214. IN PPOP_HIBER_CONTEXT HiberContext,
  215. IN ULONG Percent
  216. );
  217. VOID
  218. PopReturnMemoryForHibernate (
  219. IN PPOP_HIBER_CONTEXT HiberContext,
  220. IN BOOLEAN Unmap,
  221. IN OUT PMDL *MdlList
  222. );
  223. VOID
  224. PopAddPagesToCompressedPageSet(
  225. IN BOOLEAN AllowDataBuffering,
  226. IN PPOP_HIBER_CONTEXT HiberContext,
  227. IN OUT PULONG_PTR CompressedBufferOffset,
  228. IN PVOID StartVa,
  229. IN PFN_NUMBER NumPages,
  230. IN OUT PPFN_NUMBER SetFilePage
  231. );
  232. VOID
  233. PopEndCompressedPageSet(
  234. IN PPOP_HIBER_CONTEXT HiberContext,
  235. IN OUT PULONG_PTR CompressedBufferOffset,
  236. IN OUT PPFN_NUMBER SetFilePage
  237. );
  238. UCHAR
  239. PopGetHiberFlags(
  240. VOID
  241. );
  242. PMDL
  243. PopSplitMdl(
  244. IN PMDL Original,
  245. IN ULONG SplitPages
  246. );
  247. VOID
  248. PopZeroHiberFile(
  249. IN HANDLE FileHandle,
  250. IN PFILE_OBJECT FileObject
  251. );
  252. PVOID
  253. PopAllocateOwnMemory(
  254. IN PPOP_HIBER_CONTEXT HiberContext,
  255. IN ULONG Bytes,
  256. IN ULONG Tag
  257. );
  258. PVOID
  259. XPRESS_CALL
  260. PopAllocateHiberContextCallback(
  261. PVOID context,
  262. int CompressionWorkspaceSize
  263. );
  264. VOID
  265. PopIORegionMove (
  266. IN IOREGION *To, // ptr to region descriptor to put bytes to
  267. IN IOREGION *From, // ptr to region descriptor to get bytes from
  268. IN LONG Bytes // # of bytes to add to the end of region
  269. );
  270. BOOLEAN
  271. PopIOResume (
  272. IN PPOP_HIBER_CONTEXT HiberContext,
  273. IN BOOLEAN Complete
  274. );
  275. VOID
  276. XPRESS_CALL
  277. PopIOCallback (
  278. PVOID Context,
  279. int Compressed
  280. );
  281. VOID
  282. PopIOWrite (
  283. IN PPOP_HIBER_CONTEXT HiberContext,
  284. IN PUCHAR Ptr,
  285. IN LONG Bytes,
  286. IN PFN_NUMBER FilePage
  287. );
  288. VOID
  289. PopHiberPoolInit (
  290. PPOP_HIBER_CONTEXT HiberContext,
  291. PVOID Memory,
  292. ULONG Size
  293. );
  294. BOOLEAN
  295. PopHiberPoolCheckFree(
  296. PVOID HiberPoolPtr,
  297. PVOID BlockPtr
  298. );
  299. PVOID
  300. PopHiberPoolAllocFree (
  301. IN POOL_TYPE PoolType,
  302. IN SIZE_T NumberOfBytes,
  303. IN ULONG Tag,
  304. PVOID MemoryPtr
  305. );
  306. VOID
  307. PopDumpStatistics(
  308. IN PPO_HIBER_PERF PerfInfo
  309. );
  310. #ifdef ALLOC_PRAGMA
  311. #pragma alloc_text(PAGE, PopEnableHiberFile)
  312. #pragma alloc_text(PAGE, PopCreateHiberFile)
  313. #pragma alloc_text(PAGE, PopClearHiberFileSignature)
  314. #pragma alloc_text(PAGE, PopAllocateHiberContext)
  315. #pragma alloc_text(PAGE, PopCreateHiberLinkFile)
  316. #pragma alloc_text(PAGE, PopGetHiberFlags)
  317. #pragma alloc_text(PAGE, PopZeroHiberFile)
  318. #pragma alloc_text(PAGE, PopAllocateHiberContextCallback)
  319. #pragma alloc_text(PAGELK, PoSetHiberRange)
  320. #pragma alloc_text(PAGELK, PopGatherMemoryForHibernate)
  321. #pragma alloc_text(PAGELK, PopCloneStack)
  322. #pragma alloc_text(PAGELK, PopPreserveRange)
  323. #pragma alloc_text(PAGELK, PopCloneRange)
  324. #pragma alloc_text(PAGELK, PopDiscardRange)
  325. #pragma alloc_text(PAGELK, PopAllocatePages)
  326. #pragma alloc_text(PAGELK, PopBuildMemoryImageHeader)
  327. #pragma alloc_text(PAGELK, PopSaveHiberContext)
  328. #pragma alloc_text(PAGELK, PopWriteHiberImage)
  329. #pragma alloc_text(PAGELK, PopHiberComplete)
  330. #pragma alloc_text(PAGELK, PopSimpleRangeCheck)
  331. #pragma alloc_text(PAGELK, PopCreateDumpMdl)
  332. #pragma alloc_text(PAGELK, PopWriteHiberPages)
  333. #pragma alloc_text(PAGELK, PopUpdateHiberComplete)
  334. #pragma alloc_text(PAGELK, PopFreeHiberContext)
  335. #pragma alloc_text(PAGELK, PopReturnMemoryForHibernate)
  336. #pragma alloc_text(PAGELK, PopAddPagesToCompressedPageSet)
  337. #pragma alloc_text(PAGELK, PopEndCompressedPageSet)
  338. #pragma alloc_text(PAGELK, PopAllocateOwnMemory)
  339. #pragma alloc_text(PAGELK, PopIORegionMove)
  340. #pragma alloc_text(PAGELK, PopIOResume)
  341. #pragma alloc_text(PAGELK, PopIOCallback)
  342. #pragma alloc_text(PAGELK, PopIOWrite)
  343. #pragma alloc_text(PAGELK, PopHiberPoolInit)
  344. #pragma alloc_text(PAGELK, PopHiberPoolCheckFree)
  345. #pragma alloc_text(PAGELK, PopHiberPoolAllocFree)
  346. #pragma alloc_text(PAGELK, PopDumpStatistics)
  347. #ifdef HIBER_DEBUG
  348. #pragma alloc_text(PAGELK, PopHiberPoolVfy)
  349. #endif
  350. #endif
  351. NTSTATUS
  352. PopEnableHiberFile (
  353. IN BOOLEAN Enable
  354. )
  355. /*++
  356. Routine Description:
  357. This function commits or decommits the storage required to hold the
  358. hibernation image on the boot volume.
  359. N.B. The power policy lock must be held
  360. Arguments:
  361. Enable - TRUE if hibernation file is to be reserved; otherwise, false
  362. Return Value:
  363. Status
  364. --*/
  365. {
  366. PDUMP_STACK_CONTEXT DumpStack;
  367. NTSTATUS Status;
  368. LARGE_INTEGER FileSize;
  369. ULONG i;
  370. PFN_NUMBER NoPages;
  371. //
  372. // If this is a disable handle it
  373. //
  374. if (!Enable) {
  375. if (!PopHiberFile.FileObject) {
  376. Status = STATUS_SUCCESS;
  377. goto Done;
  378. }
  379. //
  380. // Disable hiber file
  381. //
  382. if (MmZeroPageFile) {
  383. PopZeroHiberFile(PopHiberFile.FileHandle, PopHiberFile.FileObject);
  384. }
  385. ObDereferenceObject (PopHiberFile.FileObject);
  386. ZwClose (PopHiberFile.FileHandle);
  387. ExFreePool (PopHiberFile.PagedMcb);
  388. RtlZeroMemory (&PopHiberFile, sizeof(PopHiberFile));
  389. if (PopHiberFileDebug.FileObject) {
  390. if (MmZeroPageFile) {
  391. PopZeroHiberFile(PopHiberFileDebug.FileHandle,PopHiberFileDebug.FileObject );
  392. }
  393. ObDereferenceObject (PopHiberFileDebug.FileObject);
  394. ZwClose (PopHiberFileDebug.FileHandle);
  395. RtlZeroMemory (&PopHiberFileDebug, sizeof(PopHiberFileDebug));
  396. }
  397. //
  398. // Disable hiberfile allocation
  399. //
  400. PopCapabilities.HiberFilePresent = FALSE;
  401. PopHeuristics.HiberFileEnabled = FALSE;
  402. PopHeuristics.Dirty = TRUE;
  403. //
  404. // recompute the policies and make the proper notification
  405. //
  406. PopResetCurrentPolicies ();
  407. PopSetNotificationWork (PO_NOTIFY_CAPABILITIES);
  408. Status = STATUS_SUCCESS;
  409. goto Done;
  410. }
  411. //
  412. // Enable hiber file
  413. //
  414. if (PopHiberFile.FileObject) {
  415. Status = STATUS_SUCCESS;
  416. goto Done;
  417. }
  418. //
  419. // If the hal hasn't registered an S4 handler, then it's not possible
  420. //
  421. if (!PopCapabilities.SystemS4) {
  422. Status = STATUS_NOT_SUPPORTED;
  423. goto Done;
  424. }
  425. //
  426. // Compute the size required for a hibernation file
  427. //
  428. NoPages = 0;
  429. for (i=0; i < MmPhysicalMemoryBlock->NumberOfRuns; i++) {
  430. NoPages += MmPhysicalMemoryBlock->Run[i].PageCount;
  431. }
  432. FileSize.QuadPart = (ULONGLONG) NoPages << PAGE_SHIFT;
  433. //
  434. // If we've never verified that the dumpstack loads do so now
  435. // before we allocate a huge file on the boot disk
  436. //
  437. if (!PopHeuristics.GetDumpStackVerified) {
  438. Status = IoGetDumpStack ((PWCHAR)PopDumpStackPrefix,
  439. &DumpStack,
  440. DeviceUsageTypeHibernation,
  441. (POP_IGNORE_UNSUPPORTED_DRIVERS & PopSimulate));
  442. if (!NT_SUCCESS(Status)) {
  443. goto Done;
  444. }
  445. IoFreeDumpStack (DumpStack);
  446. PopHeuristics.GetDumpStackVerified = TRUE;
  447. }
  448. //
  449. // Create the hiberfile file
  450. //
  451. Status = PopCreateHiberFile (&PopHiberFile, (PWCHAR)PopHiberFileName, &FileSize, FALSE);
  452. if (!NT_SUCCESS(Status)) {
  453. goto Done;
  454. }
  455. //
  456. // Create the debug hiberfile file
  457. //
  458. if (PopSimulate & POP_DEBUG_HIBER_FILE) {
  459. PopCreateHiberFile (&PopHiberFileDebug, (PWCHAR)PopDebugHiberFileName, &FileSize, TRUE);
  460. }
  461. //
  462. // Success
  463. //
  464. PopCapabilities.HiberFilePresent = TRUE;
  465. if (!PopHeuristics.HiberFileEnabled) {
  466. PopHeuristics.HiberFileEnabled = TRUE;
  467. PopHeuristics.Dirty = TRUE;
  468. }
  469. PopClearHiberFileSignature (FALSE);
  470. Done:
  471. PopSaveHeuristics ();
  472. return Status;
  473. }
  474. NTSTATUS
  475. PopCreateHiberFile (
  476. IN PPOP_HIBER_FILE HiberFile,
  477. IN PWCHAR NameString,
  478. IN PLARGE_INTEGER FileSize,
  479. IN BOOLEAN DebugHiberFile
  480. )
  481. {
  482. UNICODE_STRING BaseName;
  483. UNICODE_STRING HiberFileName;
  484. OBJECT_ATTRIBUTES ObjectAttributes;
  485. FILE_END_OF_FILE_INFORMATION Eof;
  486. NTSTATUS Status;
  487. IO_STATUS_BLOCK IoStatus;
  488. HANDLE FileHandle = NULL;
  489. LONGLONG McbFileSize;
  490. PFILE_OBJECT File = NULL;
  491. PDEVICE_OBJECT DeviceObject;
  492. PLARGE_INTEGER mcb;
  493. ULONG i;
  494. PUCHAR Bitmap;
  495. LARGE_INTEGER ByteOffset;
  496. KEVENT Event;
  497. PMDL Mdl;
  498. HiberFileName.Buffer = NULL;
  499. mcb = NULL;
  500. RtlInitUnicodeString (&BaseName, NameString);
  501. HiberFileName.Length = 0;
  502. HiberFileName.MaximumLength = IoArcBootDeviceName.Length + BaseName.Length;
  503. HiberFileName.Buffer = ExAllocatePoolWithTag (PagedPool|POOL_COLD_ALLOCATION,
  504. HiberFileName.MaximumLength,
  505. POP_HIBR_TAG);
  506. if (!HiberFileName.Buffer) {
  507. Status = STATUS_INSUFFICIENT_RESOURCES;
  508. goto Done;
  509. }
  510. RtlAppendUnicodeStringToString(&HiberFileName, &IoArcBootDeviceName);
  511. RtlAppendUnicodeStringToString(&HiberFileName, &BaseName);
  512. InitializeObjectAttributes(&ObjectAttributes,
  513. &HiberFileName,
  514. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  515. NULL,
  516. NULL);
  517. Status = IoCreateFile(
  518. &FileHandle,
  519. FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
  520. &ObjectAttributes,
  521. &IoStatus,
  522. FileSize,
  523. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
  524. 0L,
  525. FILE_SUPERSEDE,
  526. FILE_NO_INTERMEDIATE_BUFFERING | FILE_NO_COMPRESSION | FILE_DELETE_ON_CLOSE,
  527. (PVOID) NULL,
  528. 0L,
  529. CreateFileTypeNone,
  530. (PVOID) NULL,
  531. IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING
  532. );
  533. if (!NT_SUCCESS(Status)) {
  534. PoPrint (PO_HIBERNATE, ("PopCreateHiberFile: failed to create file %x\n", Status));
  535. goto Done;
  536. }
  537. Status = ObReferenceObjectByHandle (FileHandle,
  538. FILE_READ_DATA | FILE_WRITE_DATA,
  539. IoFileObjectType,
  540. KernelMode,
  541. (PVOID *)&File,
  542. NULL);
  543. if (!NT_SUCCESS(Status)) {
  544. goto Done;
  545. }
  546. //
  547. // Set the size
  548. //
  549. Eof.EndOfFile.QuadPart = FileSize->QuadPart;
  550. Status = ZwSetInformationFile (
  551. FileHandle,
  552. &IoStatus,
  553. &Eof,
  554. sizeof(Eof),
  555. FileEndOfFileInformation
  556. );
  557. if (Status == STATUS_PENDING) {
  558. Status = KeWaitForSingleObject(
  559. &File->Event,
  560. Executive,
  561. KernelMode,
  562. FALSE,
  563. NULL
  564. );
  565. Status = IoStatus.Status;
  566. }
  567. if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status)) {
  568. PoPrint (PO_HIBERNATE, ("PopCreateHiberFile: failed to set eof %x %x\n",
  569. Status, IoStatus.Status
  570. ));
  571. goto Done;
  572. }
  573. //
  574. // Hibernation file needs to be on the boot partition
  575. //
  576. DeviceObject = File->DeviceObject;
  577. if (!(DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION)) {
  578. Status = STATUS_UNSUCCESSFUL;
  579. goto Done;
  580. }
  581. //
  582. // Get the hiber file's layout
  583. //
  584. Status = ZwFsControlFile (
  585. FileHandle,
  586. (HANDLE) NULL,
  587. (PIO_APC_ROUTINE) NULL,
  588. (PVOID) NULL,
  589. &IoStatus,
  590. FSCTL_QUERY_RETRIEVAL_POINTERS,
  591. FileSize,
  592. sizeof (LARGE_INTEGER),
  593. &mcb,
  594. sizeof (PVOID)
  595. );
  596. if (Status == STATUS_PENDING) {
  597. Status = KeWaitForSingleObject(
  598. &File->Event,
  599. Executive,
  600. KernelMode,
  601. FALSE,
  602. NULL
  603. );
  604. Status = IoStatus.Status;
  605. }
  606. if (!NT_SUCCESS(Status)) {
  607. goto Done;
  608. }
  609. //
  610. // We have a hibernation file. Determine the number of mcbs, and perform
  611. // a simply sanity check on them.
  612. //
  613. McbFileSize = 0;
  614. for (i=0; mcb[i].QuadPart; i += 2) {
  615. McbFileSize += mcb[i].QuadPart;
  616. if (mcb[i+1].HighPart < 0) {
  617. Status = STATUS_UNSUCCESSFUL;
  618. goto Done;
  619. }
  620. }
  621. if (McbFileSize < FileSize->QuadPart) {
  622. Status = STATUS_UNSUCCESSFUL;
  623. goto Done;
  624. }
  625. HiberFile->NonPagedMcb = mcb;
  626. HiberFile->McbSize = (i+2) * sizeof(LARGE_INTEGER);
  627. HiberFile->PagedMcb = ExAllocatePoolWithTag (PagedPool|POOL_COLD_ALLOCATION,
  628. HiberFile->McbSize,
  629. POP_HIBR_TAG);
  630. if (!HiberFile->PagedMcb) {
  631. Status = STATUS_INSUFFICIENT_RESOURCES;
  632. goto Done;
  633. }
  634. memcpy (HiberFile->PagedMcb, mcb, HiberFile->McbSize);
  635. HiberFile->FileHandle = FileHandle;
  636. HiberFile->FileObject = File;
  637. HiberFile->FilePages = (PFN_NUMBER) (FileSize->QuadPart >> PAGE_SHIFT);
  638. HiberFile->McbCheck = PoSimpleCheck (0, HiberFile->PagedMcb, HiberFile->McbSize);
  639. Done:
  640. if (!NT_SUCCESS(Status)) {
  641. if (FileHandle != NULL) {
  642. ZwClose (FileHandle);
  643. }
  644. if (File != NULL) {
  645. ObDereferenceObject(File);
  646. }
  647. }
  648. if (HiberFileName.Buffer) {
  649. ExFreePool (HiberFileName.Buffer);
  650. }
  651. if (mcb && !DebugHiberFile) {
  652. HiberFile->NonPagedMcb = NULL;
  653. ExFreePool (mcb);
  654. }
  655. //
  656. // If no error, then hiber file being present change one way or another -
  657. // recompute the policies and make the proper notification
  658. //
  659. if (NT_SUCCESS(Status)) {
  660. PopResetCurrentPolicies ();
  661. PopSetNotificationWork (PO_NOTIFY_CAPABILITIES);
  662. }
  663. return Status;
  664. }
  665. NTSTATUS
  666. PopCreateHiberLinkFile (
  667. IN PPOP_HIBER_CONTEXT HiberContext
  668. )
  669. /*++
  670. Routine Description:
  671. This function creates a file on the loader partition which supplies
  672. the loader with the location of the hibernation context file
  673. Arguments:
  674. None
  675. Return Value:
  676. None
  677. --*/
  678. {
  679. UNICODE_STRING BaseName;
  680. UNICODE_STRING HiberFileName;
  681. OBJECT_ATTRIBUTES ObjectAttributes;
  682. NTSTATUS Status;
  683. IO_STATUS_BLOCK IoStatus;
  684. LARGE_INTEGER FileSize;
  685. LARGE_INTEGER ByteOffset;
  686. PPO_IMAGE_LINK LinkImage;
  687. PUCHAR Buffer;
  688. ULONG Length;
  689. HANDLE FileHandle=NULL;
  690. Buffer = NULL;
  691. RtlInitUnicodeString (&BaseName, PopHiberFileName);
  692. //
  693. // Allocate working space
  694. //
  695. Length = IoArcHalDeviceName.Length + BaseName.Length;
  696. if (Length < IoArcBootDeviceName.Length + sizeof(PO_IMAGE_LINK)) {
  697. Length = IoArcBootDeviceName.Length + sizeof(PO_IMAGE_LINK);
  698. }
  699. Buffer = ExAllocatePoolWithTag (PagedPool, Length, POP_HIBR_TAG);
  700. if (!Buffer) {
  701. Status = STATUS_INSUFFICIENT_RESOURCES;
  702. goto Done;
  703. }
  704. LinkImage = (PPO_IMAGE_LINK) Buffer;
  705. HiberFileName.Buffer = (PWCHAR) Buffer;
  706. HiberFileName.MaximumLength = (USHORT) Length;
  707. //
  708. // Open hiberfil.sys on loader partition
  709. //
  710. HiberFileName.Length = 0;
  711. RtlAppendUnicodeStringToString(&HiberFileName, &IoArcHalDeviceName);
  712. RtlAppendUnicodeStringToString(&HiberFileName, &BaseName);
  713. InitializeObjectAttributes(
  714. &ObjectAttributes,
  715. &HiberFileName,
  716. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  717. NULL,
  718. NULL
  719. );
  720. FileSize.QuadPart = 0;
  721. Status = IoCreateFile (
  722. &FileHandle,
  723. FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
  724. &ObjectAttributes,
  725. &IoStatus,
  726. &FileSize,
  727. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  728. 0,
  729. FILE_SUPERSEDE,
  730. FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_COMPRESSION | FILE_DELETE_ON_CLOSE,
  731. (PVOID) NULL,
  732. 0L,
  733. CreateFileTypeNone,
  734. (PVOID) NULL,
  735. 0
  736. );
  737. if (!NT_SUCCESS(Status)) {
  738. if (Status != STATUS_SHARING_VIOLATION && Status != STATUS_ACCESS_DENIED) {
  739. PoPrint (PO_HIBERNATE, ("PopCreateHiberLinkFile: failed to create file %x\n", Status));
  740. }
  741. //
  742. // Having a link file is nice, but it's not a requirement
  743. //
  744. Status = STATUS_SUCCESS;
  745. goto Done;
  746. }
  747. //
  748. // Write the partition name to link to
  749. //
  750. LinkImage->Signature = PO_IMAGE_SIGNATURE_LINK;
  751. Length = strlen (IoLoaderArcBootDeviceName) + 1;
  752. memcpy (LinkImage->Name, IoLoaderArcBootDeviceName, Length);
  753. ByteOffset.QuadPart = 0;
  754. Status = ZwWriteFile (
  755. FileHandle,
  756. NULL,
  757. NULL,
  758. NULL,
  759. &IoStatus,
  760. LinkImage,
  761. FIELD_OFFSET (PO_IMAGE_LINK, Name) + Length,
  762. &ByteOffset,
  763. NULL
  764. );
  765. if (!NT_SUCCESS(Status)) {
  766. goto Done;
  767. }
  768. //
  769. // Link file needs to make it to the disk
  770. //
  771. ZwFlushBuffersFile (FileHandle, &IoStatus);
  772. //
  773. // Success, keep the file around
  774. //
  775. HiberContext->LinkFile = TRUE;
  776. HiberContext->LinkFileHandle = FileHandle;
  777. Done:
  778. if (Buffer) {
  779. ExFreePool (Buffer);
  780. }
  781. if ((!NT_SUCCESS(Status)) &&
  782. (FileHandle != NULL)) {
  783. ZwClose (FileHandle);
  784. }
  785. return Status;
  786. }
  787. VOID
  788. PopClearHiberFileSignature (
  789. IN BOOLEAN GetStats
  790. )
  791. /*++
  792. Routine Description:
  793. This function sets the signature in the hibernation image to be 0,
  794. which indicates no context is contained in the image.
  795. N.B. The power policy lock must be held
  796. Arguments:
  797. GetStats - if TRUE indicates performance statistics should be read
  798. out of the hiberfile and written into the registry
  799. Return Value:
  800. None
  801. --*/
  802. {
  803. NTSTATUS Status;
  804. IO_STATUS_BLOCK IoStatus;
  805. PUCHAR Buffer;
  806. LARGE_INTEGER ByteOffset;
  807. KEVENT Event;
  808. PMDL Mdl;
  809. if (PopHiberFile.FileObject) {
  810. Buffer = ExAllocatePoolWithTag (NonPagedPool, PAGE_SIZE, POP_HIBR_TAG);
  811. if (Buffer == NULL) {
  812. return;
  813. }
  814. KeInitializeEvent(&Event, NotificationEvent, FALSE);
  815. RtlZeroMemory (Buffer, PAGE_SIZE);
  816. ByteOffset.QuadPart = 0;
  817. Mdl = MmCreateMdl (NULL, Buffer, PAGE_SIZE);
  818. MmBuildMdlForNonPagedPool (Mdl);
  819. if (GetStats) {
  820. Status = IoPageRead(PopHiberFile.FileObject,
  821. Mdl,
  822. &ByteOffset,
  823. &Event,
  824. &IoStatus);
  825. if (NT_SUCCESS(Status)) {
  826. KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  827. if (NT_SUCCESS(IoStatus.Status)) {
  828. UNICODE_STRING UnicodeString;
  829. OBJECT_ATTRIBUTES ObjectAttributes;
  830. HANDLE Handle;
  831. ULONG Data;
  832. PPO_MEMORY_IMAGE MemImage = (PPO_MEMORY_IMAGE)Buffer;
  833. RtlInitUnicodeString(&UnicodeString,
  834. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Power");
  835. InitializeObjectAttributes(&ObjectAttributes,
  836. &UnicodeString,
  837. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  838. NULL,
  839. NULL);
  840. Status = ZwOpenKey(&Handle,
  841. KEY_READ | KEY_WRITE,
  842. &ObjectAttributes);
  843. if (NT_SUCCESS(Status)) {
  844. RtlInitUnicodeString(&UnicodeString, L"HiberElapsedTime");
  845. Data = MemImage->PerfInfo.ElapsedTime;
  846. ZwSetValueKey(Handle,
  847. &UnicodeString,
  848. 0,
  849. REG_DWORD,
  850. &Data,
  851. sizeof(Data));
  852. RtlInitUnicodeString(&UnicodeString, L"HiberIoTime");
  853. Data = MemImage->PerfInfo.IoTime;
  854. ZwSetValueKey(Handle,
  855. &UnicodeString,
  856. 0,
  857. REG_DWORD,
  858. &Data,
  859. sizeof(Data));
  860. RtlInitUnicodeString(&UnicodeString, L"HiberCopyTime");
  861. Data = MemImage->PerfInfo.CopyTime;
  862. ZwSetValueKey(Handle,
  863. &UnicodeString,
  864. 0,
  865. REG_DWORD,
  866. &Data,
  867. sizeof(Data));
  868. RtlInitUnicodeString(&UnicodeString, L"HiberCopyBytes");
  869. Data = MemImage->PerfInfo.BytesCopied;
  870. ZwSetValueKey(Handle,
  871. &UnicodeString,
  872. 0,
  873. REG_DWORD,
  874. &Data,
  875. sizeof(Data));
  876. RtlInitUnicodeString(&UnicodeString, L"HiberPagesWritten");
  877. Data = MemImage->PerfInfo.PagesWritten;
  878. ZwSetValueKey(Handle,
  879. &UnicodeString,
  880. 0,
  881. REG_DWORD,
  882. &Data,
  883. sizeof(Data));
  884. RtlInitUnicodeString(&UnicodeString, L"HiberPagesProcessed");
  885. Data = MemImage->PerfInfo.PagesProcessed;
  886. ZwSetValueKey(Handle,
  887. &UnicodeString,
  888. 0,
  889. REG_DWORD,
  890. &Data,
  891. sizeof(Data));
  892. RtlInitUnicodeString(&UnicodeString, L"HiberDumpCount");
  893. Data = MemImage->PerfInfo.DumpCount;
  894. ZwSetValueKey(Handle,
  895. &UnicodeString,
  896. 0,
  897. REG_DWORD,
  898. &Data,
  899. sizeof(Data));
  900. RtlInitUnicodeString(&UnicodeString, L"HiberFileRuns");
  901. Data = MemImage->PerfInfo.FileRuns;
  902. ZwSetValueKey(Handle,
  903. &UnicodeString,
  904. 0,
  905. REG_DWORD,
  906. &Data,
  907. sizeof(Data));
  908. ZwClose(Handle);
  909. }
  910. }
  911. }
  912. }
  913. RtlZeroMemory (Buffer, PAGE_SIZE);
  914. KeClearEvent(&Event);
  915. IoSynchronousPageWrite (
  916. PopHiberFile.FileObject,
  917. Mdl,
  918. &ByteOffset,
  919. &Event,
  920. &IoStatus
  921. );
  922. KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  923. ExFreePool (Mdl);
  924. ExFreePool (Buffer);
  925. }
  926. }
  927. VOID
  928. PopZeroHiberFile(
  929. IN HANDLE FileHandle,
  930. IN PFILE_OBJECT FileObject
  931. )
  932. /*++
  933. Routine Description:
  934. Zeroes out a hibernation file completely. This is to prevent
  935. any leakage of data out of the hiberfile once it has been
  936. deleted.
  937. Arguments:
  938. FileHandle - Supplies the file handle to be zeroed.
  939. FileObject - Supplies the file object to be zeroed.
  940. Return Value:
  941. None.
  942. --*/
  943. {
  944. IO_STATUS_BLOCK IoStatusBlock;
  945. FILE_STANDARD_INFORMATION FileInfo;
  946. LARGE_INTEGER Offset;
  947. ULONGLONG Remaining;
  948. ULONG Size;
  949. PVOID Zeroes;
  950. NTSTATUS Status;
  951. PMDL Mdl;
  952. KEVENT Event;
  953. PAGED_CODE();
  954. KeInitializeEvent(&Event, NotificationEvent, FALSE);
  955. //
  956. // Get the size of the file to be zeroed
  957. //
  958. Status = ZwQueryInformationFile(FileHandle,
  959. &IoStatusBlock,
  960. &FileInfo,
  961. sizeof(FileInfo),
  962. FileStandardInformation);
  963. if (NT_SUCCESS(Status)) {
  964. //
  965. // Allocate a bunch of memory to use as zeroes
  966. //
  967. Zeroes = ExAllocatePoolWithTag(NonPagedPool,
  968. POP_ZERO_CHUNK_SIZE,
  969. 'rZoP');
  970. if (Zeroes) {
  971. RtlZeroMemory(Zeroes, POP_ZERO_CHUNK_SIZE);
  972. Mdl = MmCreateMdl(NULL, Zeroes, POP_ZERO_CHUNK_SIZE);
  973. if (Mdl) {
  974. MmBuildMdlForNonPagedPool (Mdl);
  975. Offset.QuadPart = 0;
  976. Remaining = FileInfo.AllocationSize.QuadPart;
  977. Size = POP_ZERO_CHUNK_SIZE;
  978. while (Remaining) {
  979. if (Remaining < POP_ZERO_CHUNK_SIZE) {
  980. Size = (ULONG)Remaining;
  981. Mdl = MmCreateMdl(Mdl, Zeroes, Size);
  982. MmBuildMdlForNonPagedPool(Mdl);
  983. }
  984. KeClearEvent(&Event);
  985. Status = IoSynchronousPageWrite(FileObject,
  986. Mdl,
  987. &Offset,
  988. &Event,
  989. &IoStatusBlock);
  990. if (NT_SUCCESS(Status)) {
  991. KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  992. Status = IoStatusBlock.Status;
  993. }
  994. if (!NT_SUCCESS(Status)) {
  995. PoPrint (PO_HIBERNATE | PO_ERROR,
  996. ("PopZeroHiberFile: Write of size %lx at offset %I64x failed %08lx\n",
  997. Size,
  998. Offset.QuadPart,
  999. Status));
  1000. }
  1001. Offset.QuadPart += Size;
  1002. Remaining -= Size;
  1003. }
  1004. ExFreePool (Mdl);
  1005. }
  1006. ExFreePool(Zeroes);
  1007. }
  1008. }
  1009. }
  1010. PVOID
  1011. XPRESS_CALL
  1012. PopAllocateHiberContextCallback(
  1013. PVOID context,
  1014. int CompressionWorkspaceSize
  1015. )
  1016. /*++
  1017. Routine Description:
  1018. Called by XpressEncodeCreate to allocate XpressEncodeStream.
  1019. Arguments:
  1020. context - HiberContext
  1021. CompressionWorkspaceSize - size of block to allocate
  1022. Return Value:
  1023. Pointer to allocated memory or NULL if no enough memory
  1024. --*/
  1025. {
  1026. // Allocate the memory required for the engine's workspace
  1027. return PopAllocateOwnMemory (context, CompressionWorkspaceSize, 'Xprs');
  1028. }
  1029. PVOID
  1030. PopAllocateOwnMemory(
  1031. IN PPOP_HIBER_CONTEXT HiberContext,
  1032. IN ULONG Bytes,
  1033. IN ULONG Tag
  1034. )
  1035. /*++
  1036. Routine Description:
  1037. Called to allocate memory that will not be hibernated
  1038. Arguments:
  1039. HiberContext - Pointer to POP_HIBER_CONTEXT structure
  1040. Bytes - size of memory block in bytes that
  1041. may be not aligned on page boundary
  1042. Return Value:
  1043. Address of memory block or NULL if failed (status will be set in this case)
  1044. --*/
  1045. {
  1046. PVOID Ptr;
  1047. ULONG Pages;
  1048. // Get # of full pages
  1049. Pages = (Bytes + (PAGE_SIZE-1)) >> PAGE_SHIFT;
  1050. // Allocate memory
  1051. Ptr = PopAllocatePages (HiberContext, Pages);
  1052. // Check for error
  1053. if (Ptr == NULL) {
  1054. HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
  1055. } else {
  1056. // Do not hibernate this memory
  1057. PoSetHiberRange (HiberContext,
  1058. PO_MEM_DISCARD,
  1059. Ptr,
  1060. Pages << PAGE_SHIFT,
  1061. Tag);
  1062. }
  1063. return(Ptr);
  1064. }
  1065. NTSTATUS
  1066. PopAllocateHiberContext (
  1067. VOID
  1068. )
  1069. /*++
  1070. Routine Description:
  1071. Called to allocate an initial hibernation context structure.
  1072. N.B. The power policy lock must be held
  1073. Arguments:
  1074. None
  1075. Return Value:
  1076. Status
  1077. --*/
  1078. {
  1079. PPOP_HIBER_CONTEXT HiberContext;
  1080. ULONG i, j, k;
  1081. PLIST_ENTRY NextEntry;
  1082. PDUMP_INITIALIZATION_CONTEXT DumpInit;
  1083. PFN_NUMBER NoPages;
  1084. PFN_NUMBER Length;
  1085. PLIST_ENTRY Link;
  1086. PPOP_MEMORY_RANGE Range;
  1087. ULONG result;
  1088. PHYSICAL_ADDRESS pa;
  1089. NTSTATUS Status;
  1090. PVOID p1;
  1091. PULONG BitmapBuffer;
  1092. // Compression Related
  1093. ULONG CompressionWorkspaceSize, Unused;
  1094. PAGED_CODE();
  1095. //
  1096. // Allocate space to hold the hiber context
  1097. //
  1098. Status = STATUS_SUCCESS;
  1099. HiberContext = PopAction.HiberContext;
  1100. if (!HiberContext) {
  1101. HiberContext = ExAllocatePoolWithTag (NonPagedPool,
  1102. sizeof (POP_HIBER_CONTEXT),
  1103. POP_HMAP_TAG);
  1104. if (!HiberContext) {
  1105. return STATUS_INSUFFICIENT_RESOURCES;
  1106. }
  1107. RtlZeroMemory (HiberContext, sizeof(*HiberContext));
  1108. PopAction.HiberContext = HiberContext;
  1109. InitializeListHead (&HiberContext->ClonedRanges);
  1110. KeInitializeSpinLock (&HiberContext->Lock);
  1111. }
  1112. //
  1113. // Determine what type of hiber context for this operation
  1114. // is needed
  1115. //
  1116. if (PopAction.SystemState == PowerSystemHibernate) {
  1117. //
  1118. // For a hibernate operation, the context is written
  1119. // to the hibernation file, pages need to be set aside
  1120. // for the loaders use, and any pages not needed to
  1121. // be written to the hibernation file should also be
  1122. // set aside
  1123. //
  1124. HiberContext->WriteToFile = TRUE;
  1125. HiberContext->ReserveLoaderMemory = TRUE;
  1126. HiberContext->ReserveFreeMemory = TRUE;
  1127. HiberContext->VerifyOnWake = FALSE;
  1128. } else if (PopSimulate & POP_CRC_MEMORY) {
  1129. //
  1130. // We want to checksum all of RAM during this sleep
  1131. // operation. We don't want to reserve any pages for
  1132. // anything else since the goal here is to likely look
  1133. // for somesort of corruption of failure.
  1134. //
  1135. HiberContext->WriteToFile = FALSE;
  1136. HiberContext->ReserveLoaderMemory = FALSE;
  1137. HiberContext->ReserveFreeMemory = FALSE;
  1138. HiberContext->VerifyOnWake = TRUE;
  1139. } else {
  1140. //
  1141. // A hiber context is not needed for this sleep
  1142. //
  1143. PopFreeHiberContext (TRUE);
  1144. return STATUS_SUCCESS;
  1145. }
  1146. //
  1147. // If there's an error in the current context, then we're done
  1148. //
  1149. if (!NT_SUCCESS(HiberContext->Status)) {
  1150. goto Done;
  1151. }
  1152. //
  1153. // If writting to hibernation file, get a dump driver stack
  1154. //
  1155. if (HiberContext->WriteToFile) {
  1156. //
  1157. // Get a dump stack
  1158. //
  1159. if (!HiberContext->DumpStack) {
  1160. if (!PopHiberFile.FileObject) {
  1161. Status = STATUS_NO_SUCH_FILE;
  1162. goto Done;
  1163. }
  1164. Status = IoGetDumpStack ((PWCHAR)PopDumpStackPrefix,
  1165. &HiberContext->DumpStack,
  1166. DeviceUsageTypeHibernation,
  1167. (POP_IGNORE_UNSUPPORTED_DRIVERS & PopSimulate));
  1168. if (!NT_SUCCESS(Status)) {
  1169. goto Done;
  1170. }
  1171. DumpInit = &HiberContext->DumpStack->Init;
  1172. //
  1173. // N.B. For further performance improvements it may be possible
  1174. // to set DumpInit->StallRoutine to a custom routine
  1175. // in order to do some processing while the dump driver
  1176. // is waiting pointlessly before performing some hardware
  1177. // related action (such as ISR calls).
  1178. //
  1179. }
  1180. //
  1181. // Create a link file for the loader to locate the hibernation file
  1182. //
  1183. Status = PopCreateHiberLinkFile (HiberContext);
  1184. if (!NT_SUCCESS(Status)) {
  1185. goto Done;
  1186. }
  1187. //
  1188. // Get any hibernation flags that must be visible to the osloader
  1189. //
  1190. HiberContext->HiberFlags = PopGetHiberFlags();
  1191. }
  1192. //
  1193. // Build a map of memory
  1194. //
  1195. if (HiberContext->MemoryMap.Buffer == NULL) {
  1196. PULONG BitmapBuffer;
  1197. ULONG PageCount;
  1198. //
  1199. // Initialize a bitmap describing all of physical memory.
  1200. // For now this bitmap covers from 0-MmHighestPhysicalPage.
  1201. // To support sparse memory maps more efficiently, we could break
  1202. // this up into a bitmap for each memory block run. Probably
  1203. // not a big deal, a single bitmap costs us 4K per 128MB on x86.
  1204. //
  1205. // Note that CLEAR bits in the bitmap represent what to write out.
  1206. // This is because of the way the bitmap interfaces are defined.
  1207. //
  1208. PageCount = (ULONG)((MmHighestPhysicalPage + 32) & ~31L);
  1209. PERFINFO_HIBER_ADJUST_PAGECOUNT_FOR_BBTBUFFER(&PageCount);
  1210. BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, PageCount/8, POP_HMAP_TAG);
  1211. if (BitmapBuffer == NULL) {
  1212. Status = STATUS_INSUFFICIENT_RESOURCES;
  1213. goto Done;
  1214. }
  1215. RtlInitializeBitMap(&HiberContext->MemoryMap, BitmapBuffer, PageCount);
  1216. RtlSetAllBits(&HiberContext->MemoryMap);
  1217. for (i=0; i < MmPhysicalMemoryBlock->NumberOfRuns; i++) {
  1218. PopPreserveRange(HiberContext,
  1219. MmPhysicalMemoryBlock->Run[i].BasePage,
  1220. MmPhysicalMemoryBlock->Run[i].PageCount,
  1221. POP_MEM_TAG);
  1222. }
  1223. PERFINFO_HIBER_HANDLE_BBTBUFFER_RANGE(HiberContext);
  1224. //
  1225. // Handle kernel debugger's section
  1226. //
  1227. if (!KdPitchDebugger) {
  1228. PoSetHiberRange (HiberContext,
  1229. PO_MEM_CLONE,
  1230. (PVOID) &KdTimerDifference,
  1231. 0,
  1232. POP_DEBUGGER_TAG);
  1233. }
  1234. //
  1235. // Get Mm hibernation ranges and info
  1236. //
  1237. MmHibernateInformation (HiberContext,
  1238. &HiberContext->HiberVa,
  1239. &HiberContext->HiberPte);
  1240. //
  1241. // Get hal hibernation ranges
  1242. //
  1243. HalLocateHiberRanges (HiberContext);
  1244. //
  1245. // Get the dump drivers stack hibernation ranges
  1246. //
  1247. if (HiberContext->DumpStack) {
  1248. IoGetDumpHiberRanges (HiberContext, HiberContext->DumpStack);
  1249. }
  1250. //
  1251. // Allocate pages for cloning
  1252. //
  1253. NoPages = 0;
  1254. Link = HiberContext->ClonedRanges.Flink;
  1255. while (Link != &HiberContext->ClonedRanges) {
  1256. Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
  1257. Link = Link->Flink;
  1258. NoPages += Range->EndPage - Range->StartPage;
  1259. }
  1260. //
  1261. // Add more for ranges which are expected to appear later
  1262. //
  1263. NoPages += 40 + ((KERNEL_LARGE_STACK_SIZE >> PAGE_SHIFT) + 2) * KeNumberProcessors;
  1264. Length = NoPages << PAGE_SHIFT;
  1265. //
  1266. // Allocate pages to hold clones
  1267. //
  1268. PopGatherMemoryForHibernate (HiberContext, NoPages, &HiberContext->Spares, TRUE);
  1269. //
  1270. // Slurp one page for doing non-aligned IOs
  1271. //
  1272. HiberContext->IoPage = PopAllocatePages (HiberContext, 1);
  1273. }
  1274. if (!NT_SUCCESS(HiberContext->Status)) {
  1275. goto Done;
  1276. }
  1277. //
  1278. // If the context will be written to disk, then we will
  1279. // want to use compression.
  1280. //
  1281. if(HiberContext->WriteToFile) {
  1282. // Initialize XPRESS compression engine
  1283. HiberContext->CompressionWorkspace =
  1284. (PVOID) XpressEncodeCreate (XPRESS_MAX_SIZE,
  1285. (PVOID)HiberContext,
  1286. PopAllocateHiberContextCallback,
  1287. 0);
  1288. if(!HiberContext->CompressionWorkspace) {
  1289. // Not enough memory -- failure
  1290. HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
  1291. goto Done;
  1292. }
  1293. //
  1294. // Allocate a buffer to use for compression
  1295. //
  1296. // N.B. This is actually the space alloted for a compressed page set fragment
  1297. // (a collection
  1298. // of compressed buffers that will be written out together in an optimal fashion).
  1299. //
  1300. // We add 2 pages to this fragment size in order to
  1301. // allow the compression of any given page
  1302. // (and thus the addition of its compressed buffers to the fragment) to overrun the
  1303. // compression buffer without causing any great havoc.
  1304. //
  1305. // See PopAddPagesToCompressedPageSet and PopEndCompressedPageSet for details.
  1306. //
  1307. HiberContext->CompressedWriteBuffer =
  1308. PopAllocateOwnMemory(HiberContext, (POP_COMPRESSED_PAGE_SET_SIZE + 2) << PAGE_SHIFT, 'Wbfr');
  1309. if(!HiberContext->CompressedWriteBuffer) {
  1310. goto Done;
  1311. }
  1312. // Allocate space for compressed data
  1313. HiberContext->CompressionBlock =
  1314. PopAllocateOwnMemory (HiberContext, sizeof (COMPRESSION_BLOCK), 'Cblk');
  1315. if(!HiberContext->CompressionBlock)
  1316. goto Done;
  1317. // Set first output pointer
  1318. ((PCOMPRESSION_BLOCK) HiberContext->CompressionBlock)->Ptr =
  1319. ((PCOMPRESSION_BLOCK) HiberContext->CompressionBlock)->Buffer;
  1320. // Allocate delayed IO buffer
  1321. DmaIoPtr = NULL;
  1322. {
  1323. PUCHAR Ptr;
  1324. ULONG Size = (sizeof (DmaIoPtr[0]) + IO_DUMP_WRITE_DATA_SIZE + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);
  1325. Ptr = PopAllocateOwnMemory (HiberContext, Size + IOREGION_BUFF_SIZE , 'IObk');
  1326. if (Ptr != NULL) {
  1327. // Memory layout:
  1328. // 1. DumpLocalData (temp data for WritePendingRouting preserved between Start/Resume/Finish calls)
  1329. // 2. DmaIoPtr itself
  1330. // 3. Buffers themselves
  1331. RtlZeroMemory (Ptr, Size); // Clean IO and DumpLocalData
  1332. DmaIoPtr = (DMA_IOREGIONS *) (Ptr + IO_DUMP_WRITE_DATA_SIZE);
  1333. DmaIoPtr->DumpLocalData = Ptr;
  1334. Ptr += Size;
  1335. DmaIoPtr->Free.Beg =
  1336. DmaIoPtr->Free.Ptr =
  1337. DmaIoPtr->Used.Ptr =
  1338. DmaIoPtr->Busy.Ptr =
  1339. DmaIoPtr->Used.Beg =
  1340. DmaIoPtr->Busy.Beg = Ptr;
  1341. DmaIoPtr->Free.End =
  1342. DmaIoPtr->Used.End =
  1343. DmaIoPtr->Busy.End = Ptr + IOREGION_BUFF_SIZE;
  1344. DmaIoPtr->Free.Size = IOREGION_BUFF_SIZE;
  1345. DmaIoPtr->DmaInitialized = FALSE;
  1346. DmaIoPtr->UseDma = TRUE;
  1347. }
  1348. }
  1349. }
  1350. //
  1351. // If the context is going to be written to disk, then
  1352. // get the map of the hibernation file
  1353. //
  1354. if (HiberContext->WriteToFile && !PopHiberFile.NonPagedMcb) {
  1355. //
  1356. // Since this writes to the physical sectors of the disk
  1357. // verify the check on the MCB array before doing it
  1358. //
  1359. if (PopHiberFile.McbCheck != PoSimpleCheck (0, PopHiberFile.PagedMcb, PopHiberFile.McbSize)) {
  1360. Status = STATUS_INTERNAL_ERROR;
  1361. goto Done;
  1362. }
  1363. //
  1364. // Move the MCB array to nonpaged pool
  1365. //
  1366. PopHiberFile.NonPagedMcb = ExAllocatePoolWithTag (NonPagedPool,
  1367. PopHiberFile.McbSize,
  1368. POP_HIBR_TAG);
  1369. if (!PopHiberFile.NonPagedMcb) {
  1370. Status = STATUS_INSUFFICIENT_RESOURCES;
  1371. goto Done;
  1372. }
  1373. memcpy (PopHiberFile.NonPagedMcb, PopHiberFile.PagedMcb, PopHiberFile.McbSize);
  1374. //
  1375. // Dump driver stack needs an 8 page memory block
  1376. //
  1377. DumpInit->MemoryBlock = PopAllocateOwnMemory (HiberContext,
  1378. IO_DUMP_MEMORY_BLOCK_PAGES << PAGE_SHIFT,
  1379. 'memD');
  1380. if (!DumpInit->MemoryBlock) {
  1381. goto Done;
  1382. }
  1383. //
  1384. // Remove common buffer pages from save area
  1385. //
  1386. if (DumpInit->CommonBufferSize & (PAGE_SIZE-1)) {
  1387. PopInternalAddToDumpFile( DumpInit, sizeof(DUMP_INITIALIZATION_CONTEXT), NULL, NULL, NULL, NULL );
  1388. PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
  1389. KeBugCheckEx( INTERNAL_POWER_ERROR,
  1390. 0x102,
  1391. POP_HIBER,
  1392. (ULONG_PTR)DumpInit,
  1393. (ULONG_PTR)HiberContext );
  1394. }
  1395. for (i=0; i < 2; i++) {
  1396. if (DumpInit->CommonBuffer[i]) {
  1397. PoSetHiberRange (HiberContext,
  1398. PO_MEM_DISCARD,
  1399. DumpInit->CommonBuffer[i],
  1400. DumpInit->CommonBufferSize,
  1401. POP_COMMON_BUFFER_TAG);
  1402. }
  1403. }
  1404. }
  1405. //
  1406. // From here on, no new pages are added to the map.
  1407. //
  1408. if (HiberContext->ReserveLoaderMemory && !HiberContext->LoaderMdl) {
  1409. //
  1410. // Have Mm remove enough pages from memory to allow the
  1411. // loader space when reloading the image, and remove them
  1412. // from the hiber context memory map.
  1413. //
  1414. PopGatherMemoryForHibernate (
  1415. HiberContext,
  1416. MmHiberPages,
  1417. &HiberContext->LoaderMdl,
  1418. TRUE
  1419. );
  1420. }
  1421. Done:
  1422. if (!NT_SUCCESS(Status) && NT_SUCCESS(HiberContext->Status)) {
  1423. HiberContext->Status = Status;
  1424. }
  1425. if (!NT_SUCCESS(HiberContext->Status)) {
  1426. PopFreeHiberContext (FALSE);
  1427. }
  1428. return HiberContext->Status;
  1429. }
  1430. VOID
  1431. PopFreeHiberContext (
  1432. IN BOOLEAN FreeAll
  1433. )
  1434. /*++
  1435. Routine Description:
  1436. Releases all resources allocated in the hiber context
  1437. N.B. The power policy lock must be held
  1438. Arguments:
  1439. ContextBlock - If TRUE, the hiber context structure is
  1440. freed as well
  1441. Return Value:
  1442. None.
  1443. --*/
  1444. {
  1445. PPOP_HIBER_CONTEXT HiberContext;
  1446. PPOP_MEMORY_RANGE Range;
  1447. PLIST_ENTRY Link;
  1448. PMDL Mdl;
  1449. HiberContext = PopAction.HiberContext;
  1450. if (!HiberContext) {
  1451. return ;
  1452. }
  1453. //
  1454. // Return pages gathered from mm
  1455. //
  1456. PopReturnMemoryForHibernate (HiberContext, FALSE, &HiberContext->LoaderMdl);
  1457. PopReturnMemoryForHibernate (HiberContext, TRUE, &HiberContext->Clones);
  1458. PopReturnMemoryForHibernate (HiberContext, FALSE, &HiberContext->Spares);
  1459. //
  1460. // Free the cloned range list elements
  1461. //
  1462. while (!IsListEmpty(&HiberContext->ClonedRanges)) {
  1463. Range = CONTAINING_RECORD (HiberContext->ClonedRanges.Flink, POP_MEMORY_RANGE, Link);
  1464. RemoveEntryList (&Range->Link);
  1465. ExFreePool (Range);
  1466. }
  1467. if (HiberContext->MemoryMap.Buffer) {
  1468. ExFreePool(HiberContext->MemoryMap.Buffer);
  1469. HiberContext->MemoryMap.Buffer = NULL;
  1470. }
  1471. //
  1472. // Free hiber file Mcb info
  1473. //
  1474. if (PopHiberFile.NonPagedMcb) {
  1475. ExFreePool (PopHiberFile.NonPagedMcb);
  1476. PopHiberFile.NonPagedMcb = NULL;
  1477. }
  1478. //
  1479. // If this is a total free, free the header
  1480. //
  1481. if (FreeAll) {
  1482. //
  1483. // Free resources used by dump driver
  1484. //
  1485. if (HiberContext->DumpStack) {
  1486. IoFreeDumpStack (HiberContext->DumpStack);
  1487. }
  1488. //
  1489. // If there's a link file, remove it
  1490. //
  1491. if (HiberContext->LinkFile) {
  1492. ZwClose(HiberContext->LinkFileHandle);
  1493. }
  1494. //
  1495. // Sanity check all gathered pages have been returned to Mm
  1496. //
  1497. if (HiberContext->PagesOut) {
  1498. PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
  1499. KeBugCheckEx( INTERNAL_POWER_ERROR,
  1500. 0x103,
  1501. POP_HIBER,
  1502. (ULONG_PTR)HiberContext,
  1503. 0 );
  1504. }
  1505. //
  1506. // If this is a wake, clear the signature in the image
  1507. //
  1508. if (HiberContext->Status == STATUS_WAKE_SYSTEM) {
  1509. if (PopSimulate & POP_ENABLE_HIBER_PERF) {
  1510. PopClearHiberFileSignature(TRUE);
  1511. } else {
  1512. PopClearHiberFileSignature(FALSE);
  1513. }
  1514. }
  1515. //
  1516. // Free hiber context structure itself
  1517. //
  1518. PopAction.HiberContext = NULL;
  1519. ExFreePool (HiberContext);
  1520. }
  1521. }
  1522. ULONG
  1523. PopGatherMemoryForHibernate (
  1524. IN PPOP_HIBER_CONTEXT HiberContext,
  1525. IN PFN_NUMBER NoPages,
  1526. IN PMDL *MdlList,
  1527. IN BOOLEAN Wait
  1528. )
  1529. /*++
  1530. Routine Description:
  1531. Gathers NoPages from the system for hibernation work. The
  1532. gathered pages are put onto the supplied list.
  1533. Arguments:
  1534. HiberContext - The hiber context structure
  1535. NoPages - Number of pages to gather
  1536. MdlList - Head of Mdl list to enqueue the allocated pages
  1537. Wait - TRUE if caller can wait for the pages.
  1538. Return Value:
  1539. On failure FALSE and if Wait was set the HiberContext error is
  1540. set; otheriwse, TRUE
  1541. --*/
  1542. {
  1543. ULONG Result;
  1544. PPFN_NUMBER PhysPage;
  1545. ULONG i;
  1546. ULONG_PTR Length;
  1547. PMDL Mdl;
  1548. ULONG PageCount;
  1549. Result = 0;
  1550. Length = NoPages << PAGE_SHIFT;
  1551. Mdl = ExAllocatePoolWithTag (NonPagedPool,
  1552. MmSizeOfMdl (NULL, Length),
  1553. POP_HMAP_TAG);
  1554. if (Mdl) {
  1555. //
  1556. // Call Mm to gather some pages, and keep track of how many
  1557. // we have out
  1558. //
  1559. MmInitializeMdl(Mdl, NULL, Length);
  1560. Result = MmGatherMemoryForHibernate (Mdl, Wait);
  1561. }
  1562. if (Result) {
  1563. HiberContext->PagesOut += NoPages;
  1564. PhysPage = MmGetMdlPfnArray( Mdl );
  1565. for (i=0; i < NoPages; i += PageCount) {
  1566. //
  1567. // Combine contiguous pages into a single call
  1568. // to PopDiscardRange.
  1569. //
  1570. for (PageCount = 1; (i+PageCount) < NoPages; PageCount++) {
  1571. if (PhysPage[i+PageCount-1]+1 != PhysPage[i+PageCount]) {
  1572. break;
  1573. }
  1574. }
  1575. PopDiscardRange(HiberContext, PhysPage[i], PageCount, 'htaG');
  1576. }
  1577. Mdl->Next = *MdlList;
  1578. *MdlList = Mdl;
  1579. } else {
  1580. if (Mdl) {
  1581. ExFreePool (Mdl);
  1582. }
  1583. if (Wait && NT_SUCCESS(HiberContext->Status)) {
  1584. HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
  1585. }
  1586. }
  1587. return Result;
  1588. }
  1589. VOID
  1590. PopReturnMemoryForHibernate (
  1591. IN PPOP_HIBER_CONTEXT HiberContext,
  1592. IN BOOLEAN Unmap,
  1593. IN OUT PMDL *MdlList
  1594. )
  1595. /*++
  1596. Routine Description:
  1597. Returns pages allocated from PopGatherMemoryForHibernate to
  1598. the system.
  1599. Arguments:
  1600. HiberContext - The hiber context structure
  1601. MdlList - Head of Mdl list of pages to free
  1602. Return Value:
  1603. None
  1604. --*/
  1605. {
  1606. PMDL Mdl;
  1607. while (*MdlList) {
  1608. Mdl = *MdlList;
  1609. *MdlList = Mdl->Next;
  1610. HiberContext->PagesOut -= Mdl->ByteCount >> PAGE_SHIFT;
  1611. if (Unmap) {
  1612. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  1613. }
  1614. MmReturnMemoryForHibernate (Mdl);
  1615. ExFreePool (Mdl);
  1616. }
  1617. }
  1618. VOID
  1619. PoSetHiberRange (
  1620. IN PVOID Map,
  1621. IN ULONG Flags,
  1622. IN PVOID StartVa,
  1623. IN ULONG_PTR Length,
  1624. IN ULONG Tag
  1625. )
  1626. /*++
  1627. Routine Description:
  1628. Sets the virtual range to the type supplied. If the length of
  1629. the range is zero, the entire section for the address specified
  1630. is set.
  1631. Ranges are expanded to their page boundries. (E.g., starting
  1632. addresses are rounded down, and ending addresses are rounded up)
  1633. Arguments:
  1634. HiberContext - The map to set the range in
  1635. Type - Type field for the range
  1636. Start - The starting address for the range in question
  1637. Length - The length of the range, or 0 to include an entire section
  1638. Return Value:
  1639. None.
  1640. On failure, faulure status is updated in the HiberContext structure.
  1641. --*/
  1642. {
  1643. ULONG_PTR Start;
  1644. PFN_NUMBER StartPage;
  1645. PFN_NUMBER EndPage;
  1646. PFN_NUMBER FirstPage, PhysPage;
  1647. PFN_NUMBER RunLen;
  1648. ULONG NewFlags;
  1649. PHYSICAL_ADDRESS PhysAddr;
  1650. NTSTATUS Status;
  1651. PPOP_HIBER_CONTEXT HiberContext;
  1652. ULONG SectionLength;
  1653. HiberContext = Map;
  1654. //
  1655. // If no length, include the entire section which the datum resides in
  1656. //
  1657. if (Length == 0) {
  1658. Status = MmGetSectionRange (StartVa, &StartVa, &SectionLength);
  1659. if (!NT_SUCCESS(Status)) {
  1660. PoPrint (PO_HIBERNATE, ("PoSetHiberRange: Section for %08x not found - skipped\n", StartVa));
  1661. PopInternalError (POP_HIBER);
  1662. }
  1663. Length = SectionLength;
  1664. }
  1665. //
  1666. // Turn PO_MEM_CL_OR_NCHK into just PO_MEM_CLONE
  1667. //
  1668. if (Flags & PO_MEM_CL_OR_NCHK) {
  1669. Flags &= ~PO_MEM_CL_OR_NCHK;
  1670. Flags |= PO_MEM_CLONE;
  1671. }
  1672. Start = (ULONG_PTR) StartVa;
  1673. if (Flags & PO_MEM_PAGE_ADDRESS) {
  1674. //
  1675. // Caller passed a physical page range
  1676. //
  1677. Flags &= ~PO_MEM_PAGE_ADDRESS;
  1678. PopSetRange (HiberContext,
  1679. Flags,
  1680. (PFN_NUMBER)Start,
  1681. (PFN_NUMBER)Length,
  1682. Tag);
  1683. } else {
  1684. //
  1685. // Round to page boundries
  1686. //
  1687. StartPage = (PFN_NUMBER)(Start >> PAGE_SHIFT);
  1688. EndPage = (PFN_NUMBER)((Start + Length + (PAGE_SIZE-1) & ~(PAGE_SIZE-1)) >> PAGE_SHIFT);
  1689. //
  1690. // Set all pages in the range
  1691. //
  1692. while (StartPage < EndPage) {
  1693. PhysAddr = MmGetPhysicalAddress((PVOID) (StartPage << PAGE_SHIFT));
  1694. FirstPage = (PFN_NUMBER) (PhysAddr.QuadPart >> PAGE_SHIFT);
  1695. //
  1696. // For how long the run is
  1697. //
  1698. for (RunLen=1; StartPage + RunLen < EndPage; RunLen += 1) {
  1699. PhysAddr = MmGetPhysicalAddress ((PVOID) ((StartPage + RunLen) << PAGE_SHIFT) );
  1700. PhysPage = (PFN_NUMBER) (PhysAddr.QuadPart >> PAGE_SHIFT);
  1701. if (FirstPage+RunLen != PhysPage) {
  1702. break;
  1703. }
  1704. }
  1705. //
  1706. // Set this run
  1707. //
  1708. PopSetRange (HiberContext, Flags, FirstPage, RunLen, Tag);
  1709. StartPage += RunLen;
  1710. }
  1711. }
  1712. }
  1713. VOID
  1714. PopCloneStack (
  1715. IN PPOP_HIBER_CONTEXT HiberContext
  1716. )
  1717. /*++
  1718. Routine Description:
  1719. Sets the current stack in the memory map to be a cloned range
  1720. Arguments:
  1721. HiberContext - The map to set the range in
  1722. Return Value:
  1723. None.
  1724. On failure, faulure status is updated in the HiberContext structure.
  1725. --*/
  1726. {
  1727. PKTHREAD Thread;
  1728. KIRQL OldIrql;
  1729. ULONG_PTR LowLimit;
  1730. ULONG_PTR HighLimit;
  1731. KeAcquireSpinLock (&HiberContext->Lock, &OldIrql);
  1732. //
  1733. // Add local stack to clone or disable check list
  1734. //
  1735. RtlpGetStackLimits(&LowLimit, &HighLimit);
  1736. Thread = KeGetCurrentThread();
  1737. PoSetHiberRange (HiberContext,
  1738. PO_MEM_CLONE,
  1739. (PVOID)LowLimit,
  1740. HighLimit - LowLimit,
  1741. POP_STACK_TAG);
  1742. //
  1743. // Put local processors PCR & PRCB in clone list
  1744. //
  1745. PoSetHiberRange (HiberContext,
  1746. PO_MEM_CLONE,
  1747. (PVOID) KeGetPcr(),
  1748. sizeof (KPCR),
  1749. POP_PCR_TAG );
  1750. PoSetHiberRange (HiberContext,
  1751. PO_MEM_CLONE,
  1752. KeGetCurrentPrcb(),
  1753. sizeof (KPRCB),
  1754. POP_PCRB_TAG );
  1755. KeReleaseSpinLock (&HiberContext->Lock, OldIrql);
  1756. }
  1757. VOID
  1758. PopPreserveRange(
  1759. IN PPOP_HIBER_CONTEXT HiberContext,
  1760. IN PFN_NUMBER StartPage,
  1761. IN PFN_NUMBER PageCount,
  1762. IN ULONG Tag
  1763. )
  1764. /*++
  1765. Routine Description:
  1766. Adds a physical memory range to the list of ranges to be preserved.
  1767. Arguments:
  1768. HiberContext - Supplies the hibernation context
  1769. StartPage - Supplies the beginning of the range
  1770. PageCount - Supplies the length of the range
  1771. Tag - supplies a tag to be used.
  1772. Return Value:
  1773. None.
  1774. --*/
  1775. {
  1776. //
  1777. // If this range is outside the area covered by our bitmap, then we
  1778. // will just clone it instead.
  1779. //
  1780. if (StartPage + PageCount > HiberContext->MemoryMap.SizeOfBitMap) {
  1781. PoPrint (PO_HIBERNATE,
  1782. ("PopPreserveRange: range %08lx, length %lx is outside bitmap of size %lx\n",
  1783. StartPage,
  1784. PageCount,
  1785. HiberContext->MemoryMap.SizeOfBitMap));
  1786. PopCloneRange(HiberContext, StartPage, PageCount, Tag);
  1787. return;
  1788. }
  1789. PoPrint(PO_HIBERNATE,
  1790. ("PopPreserveRange - setting page %08lx - %08lx, Tag %.4s\n",
  1791. StartPage,
  1792. StartPage + PageCount,
  1793. &Tag));
  1794. RtlClearBits(&HiberContext->MemoryMap, (ULONG)StartPage, (ULONG)PageCount);
  1795. }
  1796. VOID
  1797. PopDiscardRange(
  1798. IN PPOP_HIBER_CONTEXT HiberContext,
  1799. IN PFN_NUMBER StartPage,
  1800. IN PFN_NUMBER PageCount,
  1801. IN ULONG Tag
  1802. )
  1803. /*++
  1804. Routine Description:
  1805. Removes a physical memory range from the list of ranges to be preserved.
  1806. Arguments:
  1807. HiberContext - Supplies the hibernation context
  1808. StartPage - Supplies the beginning of the range
  1809. PageCount - Supplies the length of the range
  1810. Tag - supplies a tag to be used.
  1811. Return Value:
  1812. None.
  1813. --*/
  1814. {
  1815. PFN_NUMBER sp;
  1816. PFN_NUMBER count;
  1817. //
  1818. // If this range is outside the area covered by our bitmap, then
  1819. // it's not going to get written anyway.
  1820. //
  1821. if (StartPage <= HiberContext->MemoryMap.SizeOfBitMap) {
  1822. sp = StartPage;
  1823. count = PageCount;
  1824. if (sp + count > HiberContext->MemoryMap.SizeOfBitMap) {
  1825. //
  1826. // trim PageCount
  1827. //
  1828. count = HiberContext->MemoryMap.SizeOfBitMap - sp;
  1829. }
  1830. PoPrint(PO_HIBERNATE,
  1831. ("PopDiscardRange - removing page %08lx - %08lx, Tag %.4s\n",
  1832. StartPage,
  1833. StartPage + PageCount,
  1834. &Tag));
  1835. RtlSetBits(&HiberContext->MemoryMap, (ULONG)sp, (ULONG)count);
  1836. }
  1837. }
  1838. VOID
  1839. PopCloneRange(
  1840. IN PPOP_HIBER_CONTEXT HiberContext,
  1841. IN PFN_NUMBER StartPage,
  1842. IN PFN_NUMBER PageCount,
  1843. IN ULONG Tag
  1844. )
  1845. /*++
  1846. Routine Description:
  1847. Adds a physical memory range from the list of ranges to be cloned.
  1848. This means removing it from the list to be written and adding
  1849. an entry in the clone list.
  1850. Arguments:
  1851. HiberContext - Supplies the hibernation context
  1852. StartPage - Supplies the beginning of the range
  1853. PageCount - Supplies the length of the range
  1854. Tag - supplies a tag to be used.
  1855. Return Value:
  1856. None.
  1857. --*/
  1858. {
  1859. PLIST_ENTRY Link;
  1860. PPOP_MEMORY_RANGE Range;
  1861. PFN_NUMBER EndPage;
  1862. PoPrint(PO_HIBERNATE,
  1863. ("PopCloneRange - cloning page %08lx - %08lx, Tag %.4s\n",
  1864. StartPage,
  1865. StartPage + PageCount,
  1866. &Tag));
  1867. PopDiscardRange(HiberContext, StartPage, PageCount, Tag);
  1868. EndPage = StartPage + PageCount;
  1869. //
  1870. // Go through the range list. If we find an adjacent range, coalesce.
  1871. // Otherwise, insert a new range entry in sorted order.
  1872. //
  1873. Link = HiberContext->ClonedRanges.Flink;
  1874. while (Link != &HiberContext->ClonedRanges) {
  1875. Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
  1876. //
  1877. // Check for an overlapping or adjacent range.
  1878. //
  1879. if (((StartPage >= Range->StartPage) && (StartPage <= Range->EndPage)) ||
  1880. ((EndPage >= Range->StartPage) && (EndPage <= Range->EndPage)) ||
  1881. ((StartPage <= Range->StartPage) && (EndPage >= Range->EndPage))) {
  1882. PoPrint(PO_HIBERNATE,
  1883. ("PopCloneRange - coalescing range %lx - %lx (%.4s) with range %lx - %lx\n",
  1884. StartPage,
  1885. EndPage,
  1886. &Tag,
  1887. Range->StartPage,
  1888. Range->EndPage));
  1889. //
  1890. // Coalesce this range.
  1891. //
  1892. if (StartPage < Range->StartPage) {
  1893. Range->StartPage = StartPage;
  1894. }
  1895. if (EndPage > Range->EndPage) {
  1896. Range->EndPage = EndPage;
  1897. }
  1898. return;
  1899. }
  1900. if (Range->StartPage >= StartPage) {
  1901. //
  1902. // We have found a range greater than the current one. Insert the new range
  1903. // in this position.
  1904. //
  1905. break;
  1906. }
  1907. Link = Link->Flink;
  1908. }
  1909. //
  1910. // An adjacent range was not found. Allocate a new entry and insert
  1911. // it in front of the Link entry.
  1912. //
  1913. Range = ExAllocatePoolWithTag (NonPagedPool,
  1914. sizeof (POP_MEMORY_RANGE),
  1915. POP_HMAP_TAG);
  1916. if (!Range) {
  1917. if (NT_SUCCESS(HiberContext->Status)) {
  1918. HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
  1919. }
  1920. return ;
  1921. }
  1922. Range->Tag = Tag;
  1923. Range->StartPage = StartPage;
  1924. Range->EndPage = EndPage;
  1925. InsertTailList(Link, &Range->Link);
  1926. ++HiberContext->ClonedRangeCount;
  1927. return;
  1928. }
  1929. ULONG
  1930. PopGetRangeCount(
  1931. IN PPOP_HIBER_CONTEXT HiberContext
  1932. )
  1933. /*++
  1934. Routine Description:
  1935. Counts the number of ranges to be written out. This includes
  1936. the number of cloned ranges on the cloned range list and the
  1937. number of runs in the memory map.
  1938. Arguments:
  1939. HiberContext - Supplies the hibernation context.
  1940. Return Value:
  1941. Number of ranges to be written out.
  1942. --*/
  1943. {
  1944. ULONG RunCount=0;
  1945. ULONG NextPage=0;
  1946. ULONG Length;
  1947. while (NextPage < HiberContext->MemoryMap.SizeOfBitMap) {
  1948. Length = RtlFindNextForwardRunClear(&HiberContext->MemoryMap,
  1949. NextPage,
  1950. &NextPage);
  1951. NextPage += Length;
  1952. ++RunCount;
  1953. }
  1954. return(RunCount + HiberContext->ClonedRangeCount);
  1955. }
  1956. VOID
  1957. PopSetRange (
  1958. IN PPOP_HIBER_CONTEXT HiberContext,
  1959. IN ULONG Flags,
  1960. IN PFN_NUMBER StartPage,
  1961. IN PFN_NUMBER PageCount,
  1962. IN ULONG Tag
  1963. )
  1964. /*++
  1965. Routine Description:
  1966. Sets the specified physical range in the memory map
  1967. Arguments:
  1968. HiberContext - The map to set the range in
  1969. Type - Type to set the range too
  1970. StartPage - The first page of the range
  1971. PageCount - The length of the range in pages
  1972. Return Value:
  1973. None.
  1974. On failure, faulure status is updated in the HiberContext structure.
  1975. --*/
  1976. {
  1977. PoPrint (PO_HIBERNATE,
  1978. ("PopSetRange: Ty %04x Sp %08x Len %08x %.4s\n",
  1979. Flags,
  1980. StartPage,
  1981. PageCount,
  1982. &Tag));
  1983. if (HiberContext->MapFrozen) {
  1984. PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
  1985. KeBugCheckEx( INTERNAL_POWER_ERROR,
  1986. 0x104,
  1987. POP_HIBER,
  1988. (ULONG_PTR)HiberContext,
  1989. 0 );
  1990. }
  1991. //
  1992. // Make sure flags which should have been cleared by now aren't still set.
  1993. //
  1994. ASSERT(!(Flags & (PO_MEM_PAGE_ADDRESS | PO_MEM_CL_OR_NCHK)));
  1995. if (Flags & PO_MEM_DISCARD) {
  1996. PopDiscardRange(HiberContext, StartPage, PageCount, Tag);
  1997. } else if (Flags & PO_MEM_CLONE) {
  1998. PopCloneRange(HiberContext, StartPage, PageCount, Tag);
  1999. } else if (Flags & PO_MEM_PRESERVE) {
  2000. PopPreserveRange(HiberContext, StartPage, PageCount, Tag);
  2001. } else {
  2002. ASSERT(FALSE);
  2003. PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
  2004. KeBugCheckEx( INTERNAL_POWER_ERROR,
  2005. 0x105,
  2006. POP_HIBER,
  2007. (ULONG_PTR)HiberContext,
  2008. 0 );
  2009. }
  2010. }
  2011. VOID
  2012. PopResetRangeEnum(
  2013. IN PPOP_HIBER_CONTEXT HiberContext
  2014. )
  2015. /*++
  2016. Routine Description:
  2017. Resets the range enumerator to start at the first range.
  2018. Arguments:
  2019. HiberContext - Supplies the hibernation context
  2020. Return Value:
  2021. None
  2022. --*/
  2023. {
  2024. HiberContext->NextCloneRange = HiberContext->ClonedRanges.Flink;
  2025. HiberContext->NextPreserve = 0;
  2026. }
  2027. VOID
  2028. PopGetNextRange(
  2029. IN PPOP_HIBER_CONTEXT HiberContext,
  2030. OUT PPFN_NUMBER StartPage,
  2031. OUT PPFN_NUMBER EndPage,
  2032. OUT PVOID *CloneVa
  2033. )
  2034. /*++
  2035. Routine Description:
  2036. Enumerates the next range to be written to the hibernation file
  2037. Arguments:
  2038. HiberContext - Supplies the hibernation context.
  2039. StartPage - Returns the starting physical page to be written.
  2040. EndPage - Returns the ending physical page (non-inclusive) to be written
  2041. CloneVa - If the range is to be cloned, returns the cloned virtual address
  2042. If the range is not cloned, returns NULL
  2043. Return Value:
  2044. NTSTATUS
  2045. --*/
  2046. {
  2047. PPOP_MEMORY_RANGE Range;
  2048. ULONG Length;
  2049. ULONG StartIndex;
  2050. if (HiberContext->NextCloneRange != &HiberContext->ClonedRanges) {
  2051. //
  2052. // Return the next cloned range
  2053. //
  2054. Range = CONTAINING_RECORD(HiberContext->NextCloneRange, POP_MEMORY_RANGE, Link);
  2055. HiberContext->NextCloneRange = HiberContext->NextCloneRange->Flink;
  2056. *StartPage = Range->StartPage;
  2057. *EndPage = Range->EndPage;
  2058. *CloneVa = Range->CloneVa;
  2059. ASSERT(Range->CloneVa != NULL);
  2060. } else {
  2061. //
  2062. // We have enumerated all the clone ranges, return the next preserved range
  2063. //
  2064. Length = RtlFindNextForwardRunClear(&HiberContext->MemoryMap,
  2065. (ULONG)HiberContext->NextPreserve,
  2066. &StartIndex);
  2067. *StartPage = StartIndex;
  2068. *EndPage = *StartPage + Length;
  2069. HiberContext->NextPreserve = *EndPage;
  2070. *CloneVa = NULL;
  2071. }
  2072. return;
  2073. }
  2074. PVOID
  2075. PopAllocatePages (
  2076. IN PPOP_HIBER_CONTEXT HiberContext,
  2077. IN PFN_NUMBER NoPages
  2078. )
  2079. /*++
  2080. Routine Description:
  2081. Allocates memory pages from system with virtual mappings.
  2082. Pages are kept on a list and are automatically freed by
  2083. PopFreeHiberContext.
  2084. Arguments:
  2085. NoPages - No of pages to allocate
  2086. Flags - Flags for the returned pages in the physical memory map
  2087. Return Value:
  2088. Virtual address of the requested pages
  2089. --*/
  2090. {
  2091. PUCHAR Buffer=NULL;
  2092. PMDL Mdl;
  2093. ULONG result;
  2094. ULONG SpareCount;
  2095. for (; ;) {
  2096. //
  2097. // If page is available in mapped clone page list, get it
  2098. //
  2099. if (NoPages < HiberContext->NoClones) {
  2100. Buffer = HiberContext->NextClone;
  2101. HiberContext->NoClones -= NoPages;
  2102. HiberContext->NextClone += NoPages << PAGE_SHIFT;
  2103. break;
  2104. }
  2105. //
  2106. // Need more virtual address space
  2107. //
  2108. if (HiberContext->Spares) {
  2109. //
  2110. // Turn spares into virtually mapped pages. Try to limit the
  2111. // number of pages being mapped so we don't run out of PTEs
  2112. // on large memory machines.
  2113. //
  2114. if ((NoPages << PAGE_SHIFT) > PO_MAX_MAPPED_CLONES) {
  2115. SpareCount = (ULONG) (NoPages << PAGE_SHIFT);
  2116. } else {
  2117. SpareCount = PO_MAX_MAPPED_CLONES;
  2118. }
  2119. Mdl = HiberContext->Spares;
  2120. if (Mdl->ByteCount > SpareCount) {
  2121. //
  2122. // Split out a smaller MDL from the spare since it is larger
  2123. // than we really need.
  2124. //
  2125. Mdl = PopSplitMdl(Mdl, SpareCount >> PAGE_SHIFT);
  2126. if (Mdl == NULL) {
  2127. break;
  2128. }
  2129. } else {
  2130. //
  2131. // Map the entire spare MDL
  2132. //
  2133. HiberContext->Spares = Mdl->Next;
  2134. }
  2135. Mdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
  2136. HiberContext->NextClone = MmMapLockedPages (Mdl, KernelMode);
  2137. if (HiberContext->NextClone == NULL) {
  2138. //
  2139. // Put the MDL back on the spare list so it gets cleaned up
  2140. // correctly by PopFreeHiberContext.
  2141. //
  2142. Mdl->Next = HiberContext->Spares;
  2143. HiberContext->Spares = Mdl;
  2144. break;
  2145. }
  2146. HiberContext->NoClones = Mdl->ByteCount >> PAGE_SHIFT;
  2147. Mdl->Next = HiberContext->Clones;
  2148. HiberContext->Clones = Mdl;
  2149. } else {
  2150. //
  2151. // No spares, allocate more
  2152. //
  2153. result = PopGatherMemoryForHibernate (HiberContext,
  2154. NoPages*2,
  2155. &HiberContext->Spares,
  2156. TRUE);
  2157. if (!result) {
  2158. break;
  2159. }
  2160. }
  2161. }
  2162. //
  2163. // If there's a failure, mark it now
  2164. //
  2165. if (!Buffer && NT_SUCCESS(HiberContext->Status)) {
  2166. HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
  2167. }
  2168. return Buffer;
  2169. }
  2170. ULONG
  2171. PopSimpleRangeCheck (
  2172. PPOP_MEMORY_RANGE Range
  2173. )
  2174. /*++
  2175. Routine Description:
  2176. Computes a checksum for the supplied range
  2177. Arguments:
  2178. Range - The range to compute the checksum for
  2179. Return Value:
  2180. The checksum value
  2181. --*/
  2182. {
  2183. PHYSICAL_ADDRESS PhysAddr;
  2184. PFN_NUMBER sp, ep, PageLen;
  2185. ULONG Check;
  2186. DUMP_MDL DumpMdl;
  2187. PMDL Mdl;
  2188. sp = Range->StartPage;
  2189. ep = Range->EndPage;
  2190. Mdl = (PMDL) DumpMdl;
  2191. if (Range->CloneVa) {
  2192. return PoSimpleCheck (0, Range->CloneVa, (ep-sp) << PAGE_SHIFT);
  2193. }
  2194. Check = 0;
  2195. while (sp < ep) {
  2196. PopCreateDumpMdl (Mdl, sp, ep);
  2197. Check = PoSimpleCheck (Check, Mdl->MappedSystemVa, Mdl->ByteCount);
  2198. sp += Mdl->ByteCount >> PAGE_SHIFT;
  2199. }
  2200. return Check;
  2201. }
  2202. VOID
  2203. PopCreateDumpMdl (
  2204. IN OUT PMDL Mdl,
  2205. IN PFN_NUMBER StartPage,
  2206. IN PFN_NUMBER EndPage
  2207. )
  2208. /*++
  2209. Routine Description:
  2210. Builds a dump MDl for the supplied starting address for
  2211. as many pages as can be mapped, or until EndPage is hit.
  2212. Arguments:
  2213. StartPage - The first page to map
  2214. EndPage - The ending page
  2215. Return Value:
  2216. Mdl
  2217. --*/
  2218. {
  2219. PFN_NUMBER Pages;
  2220. PPFN_NUMBER PhysPage;
  2221. // mapping better make sense
  2222. if (StartPage >= EndPage) {
  2223. PopInternalError (POP_HIBER);
  2224. }
  2225. Pages = EndPage - StartPage;
  2226. if (Pages > POP_MAX_MDL_SIZE) {
  2227. Pages = POP_MAX_MDL_SIZE;
  2228. }
  2229. MmInitializeMdl(Mdl, NULL, (Pages << PAGE_SHIFT));
  2230. PhysPage = MmGetMdlPfnArray( Mdl );
  2231. while (Pages) {
  2232. *PhysPage++ = StartPage++;
  2233. Pages -= 1;
  2234. }
  2235. MmMapMemoryDumpMdl (Mdl);
  2236. Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
  2237. // byte count must be a multiple of page size
  2238. if (Mdl->ByteCount & (PAGE_SIZE-1)) {
  2239. PopInternalAddToDumpFile( Mdl, sizeof(MDL), NULL, NULL, NULL, NULL );
  2240. KeBugCheckEx( INTERNAL_POWER_ERROR,
  2241. 0x106,
  2242. POP_HIBER,
  2243. (ULONG_PTR)Mdl,
  2244. 0 );
  2245. }
  2246. }
  2247. VOID
  2248. PopHiberComplete (
  2249. IN NTSTATUS Status,
  2250. IN PPOP_HIBER_CONTEXT HiberContext
  2251. )
  2252. {
  2253. //
  2254. // If the return from the hal is STATUS_DEVICE_DOES_NOT_EXIST, then
  2255. // the hal doesn't know how to power off the machine.
  2256. //
  2257. if (Status == STATUS_DEVICE_DOES_NOT_EXIST) {
  2258. if (InbvIsBootDriverInstalled()) {
  2259. // Display system shut down screen
  2260. PUCHAR Bitmap1, Bitmap2;
  2261. Bitmap1 = InbvGetResourceAddress(3); // shutdown bitmap
  2262. Bitmap2 = InbvGetResourceAddress(5); // logo bitmap
  2263. InbvSolidColorFill(190,279,468,294,0);
  2264. if (Bitmap1 && Bitmap2) {
  2265. InbvBitBlt(Bitmap1, 215, 282);
  2266. InbvBitBlt(Bitmap2, 217, 111);
  2267. }
  2268. } else {
  2269. InbvDisplayString ("State saved, power off the system\n");
  2270. }
  2271. // If reseting, set the flag and return
  2272. if (PopSimulate & POP_RESET_ON_HIBER) {
  2273. HiberContext->Reset = TRUE;
  2274. return ;
  2275. }
  2276. // done... wait for power off
  2277. for (; ;) ;
  2278. }
  2279. //
  2280. // If the image is complete or the sleep completed without error,
  2281. // then the checksums are valid
  2282. //
  2283. if ((NT_SUCCESS(Status) ||
  2284. HiberContext->MemoryImage->Signature == PO_IMAGE_SIGNATURE) &&
  2285. HiberContext->VerifyOnWake) {
  2286. }
  2287. //
  2288. // Release the dump PTEs
  2289. //
  2290. MmReleaseDumpAddresses (POP_MAX_MDL_SIZE);
  2291. //
  2292. // Hiber no longer in process
  2293. //
  2294. PoHiberInProgress = FALSE;
  2295. HiberContext->Status = Status;
  2296. }
  2297. NTSTATUS
  2298. PopBuildMemoryImageHeader (
  2299. IN PPOP_HIBER_CONTEXT HiberContext,
  2300. IN SYSTEM_POWER_STATE SystemState
  2301. )
  2302. /*++
  2303. Routine Description:
  2304. Converts the memory map range list to a memory image structure
  2305. with a range list array. This is done to build the initial image
  2306. of the header to be written into the hibernation file, and to get
  2307. the header into one chunk of pool which is not in any other
  2308. listed range list
  2309. Arguments:
  2310. HiberContext -
  2311. SystemState -
  2312. Return Value:
  2313. Status
  2314. --*/
  2315. {
  2316. PPOP_MEMORY_RANGE Range;
  2317. PPO_MEMORY_IMAGE MemImage;
  2318. PLIST_ENTRY Link;
  2319. PFN_NUMBER Length;
  2320. PFN_NUMBER StartPage;
  2321. ULONG StartIndex;
  2322. ULONG Index;
  2323. PMDL Mdl;
  2324. PPO_MEMORY_RANGE_ARRAY Table;
  2325. ULONG TablePages;
  2326. ULONG NeededPages;
  2327. ULONG NoPages, i;
  2328. ULONG result;
  2329. //
  2330. // Allocate memory image structure
  2331. //
  2332. MemImage = PopAllocatePages (HiberContext, 1);
  2333. if (!MemImage) {
  2334. return STATUS_INSUFFICIENT_RESOURCES;
  2335. }
  2336. PoSetHiberRange (HiberContext,
  2337. PO_MEM_CLONE,
  2338. MemImage,
  2339. sizeof(*MemImage),
  2340. POP_MEMIMAGE_TAG);
  2341. RtlZeroMemory(MemImage, PAGE_SIZE);
  2342. HiberContext->MemoryImage = MemImage;
  2343. MemImage->PageSize = PAGE_SIZE;
  2344. MemImage->LengthSelf = sizeof(*MemImage);
  2345. MemImage->PageSelf = (PFN_NUMBER) MmGetPhysicalAddress(MemImage).QuadPart >> PAGE_SHIFT;
  2346. KeQuerySystemTime (&MemImage->SystemTime);
  2347. MemImage->InterruptTime = KeQueryInterruptTime();
  2348. MemImage->HiberVa = HiberContext->HiberVa;
  2349. MemImage->HiberPte = HiberContext->HiberPte;
  2350. MemImage->NoHiberPtes = POP_MAX_MDL_SIZE;
  2351. MemImage->FeatureFlags = KeFeatureBits;
  2352. MemImage->ImageType = KeProcessorArchitecture;
  2353. MemImage->HiberFlags = HiberContext->HiberFlags;
  2354. if (HiberContext->LoaderMdl) {
  2355. MemImage->NoFreePages = HiberContext->LoaderMdl->ByteCount >> PAGE_SHIFT;
  2356. }
  2357. //
  2358. // Allocate storage for clones
  2359. //
  2360. Link = HiberContext->ClonedRanges.Flink;
  2361. while (Link != &HiberContext->ClonedRanges) {
  2362. Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
  2363. Link = Link->Flink;
  2364. //
  2365. // Allocate space to make a copy of this clone
  2366. //
  2367. Length = Range->EndPage - Range->StartPage;
  2368. Range->CloneVa = PopAllocatePages(HiberContext, Length);
  2369. if (!Range->CloneVa) {
  2370. PoPrint (PO_HIBERNATE, ("PopBuildImage: Could not allocate clone for %08x - %08x\n",
  2371. Range->StartPage,
  2372. Range->EndPage));
  2373. return(STATUS_INSUFFICIENT_RESOURCES);
  2374. }
  2375. }
  2376. //
  2377. // We need to build the restoration map of the pages which need to
  2378. // be saved. These table pages can't be checksum in the normal
  2379. // way as they hold the checksums for the rest of memory, so they
  2380. // are allocated as ranges with no checksum and then checksums
  2381. // are explicitly added in each page. However, allocating these
  2382. // pages may change the memory map, so we need to loop until we've
  2383. // got enough storage for the restoration tables allocated in the
  2384. // memory map to contain the tables, etc..
  2385. //
  2386. TablePages = 0;
  2387. for (; ;) {
  2388. //
  2389. // Compute table pages needed, if we have enough allocated
  2390. // then freeze the memory map and build them
  2391. //
  2392. NoPages = (PopGetRangeCount(HiberContext) + PO_ENTRIES_PER_PAGE - 1) / PO_ENTRIES_PER_PAGE;
  2393. if (NoPages <= TablePages) {
  2394. break;
  2395. }
  2396. //
  2397. // Allocate more table pages
  2398. //
  2399. NeededPages = NoPages - TablePages;
  2400. Table = PopAllocatePages(HiberContext, NeededPages);
  2401. if (!Table) {
  2402. return STATUS_INSUFFICIENT_RESOURCES;
  2403. }
  2404. for (i=0; i<NeededPages; i++) {
  2405. Table[0].Link.EntryCount = 0;
  2406. Table[0].Link.NextTable = 0;
  2407. Table[0].Link.CheckSum = 1;
  2408. Table[0].Link.Next = HiberContext->TableHead;
  2409. HiberContext->TableHead = Table;
  2410. Table = (PPO_MEMORY_RANGE_ARRAY)((ULONG_PTR)Table + PAGE_SIZE);
  2411. }
  2412. TablePages += NeededPages;
  2413. }
  2414. //
  2415. // Freeze the memory map
  2416. //
  2417. HiberContext->MapFrozen = TRUE;
  2418. //
  2419. // Fill in the ranges on the table pages
  2420. //
  2421. Table = HiberContext->TableHead;
  2422. Index = 0;
  2423. //
  2424. // Add the cloned ranges first.
  2425. //
  2426. Link = HiberContext->ClonedRanges.Flink;
  2427. while (Link != &HiberContext->ClonedRanges) {
  2428. Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
  2429. Link = Link->Flink;
  2430. PoPrint (PO_HIBER_MAP, ("PopSave: Cloned Table %08x - %08x\n",
  2431. Range->StartPage,
  2432. Range->EndPage));
  2433. Index += 1;
  2434. if (Index >= PO_MAX_RANGE_ARRAY) {
  2435. //
  2436. // Table is full, next
  2437. //
  2438. Table[0].Link.EntryCount = PO_MAX_RANGE_ARRAY-1;
  2439. Table = Table[0].Link.Next;
  2440. if (!Table) {
  2441. PopInternalError (POP_HIBER);
  2442. }
  2443. Index = 1;
  2444. }
  2445. Table[Index].Range.PageNo = 0;
  2446. Table[Index].Range.StartPage = Range->StartPage;
  2447. Table[Index].Range.EndPage = Range->EndPage;
  2448. Table[Index].Range.CheckSum = 0;
  2449. }
  2450. //
  2451. // Now add the ranges to be preserved
  2452. //
  2453. Length = RtlFindFirstRunClear(&HiberContext->MemoryMap, &StartIndex);
  2454. StartPage = StartIndex;
  2455. while (StartPage < HiberContext->MemoryMap.SizeOfBitMap) {
  2456. Index += 1;
  2457. if (Index >= PO_MAX_RANGE_ARRAY) {
  2458. //
  2459. // Table is full, next
  2460. //
  2461. Table[0].Link.EntryCount = PO_MAX_RANGE_ARRAY-1;
  2462. Table = Table[0].Link.Next;
  2463. if (!Table) {
  2464. PopInternalError (POP_HIBER);
  2465. }
  2466. Index = 1;
  2467. }
  2468. Table[Index].Range.PageNo = 0;
  2469. Table[Index].Range.StartPage = StartPage;
  2470. Table[Index].Range.EndPage = StartPage + Length;
  2471. Table[Index].Range.CheckSum = 0;
  2472. //
  2473. // Handle the corner case where the last run exactly matches
  2474. // the end of the bitmap.
  2475. //
  2476. if (StartPage + Length == HiberContext->MemoryMap.SizeOfBitMap) {
  2477. break;
  2478. }
  2479. Length = RtlFindNextForwardRunClear(&HiberContext->MemoryMap,
  2480. (ULONG)(StartPage + Length),
  2481. &StartIndex);
  2482. StartPage = StartIndex;
  2483. }
  2484. Table[0].Link.EntryCount = Index;
  2485. return HiberContext->Status;
  2486. }
  2487. NTSTATUS
  2488. PopSaveHiberContext (
  2489. IN PPOP_HIBER_CONTEXT HiberContext
  2490. )
  2491. /*++
  2492. Routine Description:
  2493. Called at HIGH_LEVEL just before the sleep operation to
  2494. make a snap shot of the system memory as defined by
  2495. the memory image array. Cloning and applying
  2496. checksum of the necessary pages occurs here.
  2497. Arguments:
  2498. HiberContext - The memory map
  2499. Return Value:
  2500. Status
  2501. --*/
  2502. {
  2503. POP_MCB_CONTEXT CurrentMcb;
  2504. PPO_MEMORY_IMAGE MemImage;
  2505. PPOP_MEMORY_RANGE Range;
  2506. PPO_MEMORY_RANGE_ARRAY Table;
  2507. ULONG Index;
  2508. PFN_NUMBER sp, ep;
  2509. DUMP_MDL DumpMdl;
  2510. PMDL Mdl;
  2511. PUCHAR cp;
  2512. PLIST_ENTRY Link;
  2513. PFN_NUMBER PageNo;
  2514. PFN_NUMBER Pages;
  2515. NTSTATUS Status;
  2516. PPFN_NUMBER TablePage;
  2517. ULONG i;
  2518. ULONGLONG StartCount;
  2519. // Compression related
  2520. ULONG CompressedSize;
  2521. //
  2522. // Hal had better have interrupts disabled here
  2523. //
  2524. if (KeDisableInterrupts() != FALSE) {
  2525. PopInternalError (POP_HIBER);
  2526. }
  2527. MemImage = HiberContext->MemoryImage;
  2528. HiberContext->CurrentMcb = &CurrentMcb;
  2529. //
  2530. // Get the current state of the processor
  2531. //
  2532. RtlZeroMemory(&PoWakeState, sizeof(KPROCESSOR_STATE));
  2533. KeSaveStateForHibernate(&PoWakeState);
  2534. HiberContext->WakeState = &PoWakeState;
  2535. //
  2536. // If there's something already in the memory image signature then
  2537. // the system is now waking up.
  2538. //
  2539. if (MemImage->Signature) {
  2540. //
  2541. // If the debugger was active, reset it
  2542. //
  2543. if (KdDebuggerEnabled && !KdPitchDebugger) {
  2544. KdDebuggerEnabled = FALSE;
  2545. KdInitSystem (0, NULL);
  2546. }
  2547. //
  2548. // Loader feature to breakin to the debugger when someone
  2549. // presses the space bar while coming back from hibernate
  2550. //
  2551. if (KdDebuggerEnabled) {
  2552. if (MemImage->Signature == PO_IMAGE_SIGNATURE_BREAK)
  2553. {
  2554. DbgBreakPoint();
  2555. }
  2556. //
  2557. // Notify the debugger we are coming back from hibernate
  2558. //
  2559. }
  2560. return STATUS_WAKE_SYSTEM;
  2561. }
  2562. //
  2563. // Set a non-zero value in the signature for the next time
  2564. //
  2565. MemImage->Signature += 1;
  2566. //
  2567. // Initialize hibernation driver stack
  2568. //
  2569. // N.B. We must reset the display and do any INT10 here. Otherwise
  2570. // the realmode stack in the HAL will get used to do the callback
  2571. // later and that memory will be modified.
  2572. //
  2573. if (HiberContext->WriteToFile) {
  2574. if (InbvIsBootDriverInstalled()) {
  2575. PUCHAR Bitmap1, Bitmap2;
  2576. Bitmap1 = InbvGetResourceAddress(2); // hibernation bitmap
  2577. Bitmap2 = InbvGetResourceAddress(5); // logo bitmap
  2578. InbvEnableDisplayString(TRUE);
  2579. InbvAcquireDisplayOwnership();
  2580. InbvResetDisplay(); // required to reset display
  2581. InbvSolidColorFill(0,0,639,479,0);
  2582. if (Bitmap1 && Bitmap2) {
  2583. InbvBitBlt(Bitmap1, 190, 279);
  2584. InbvBitBlt(Bitmap2, 217, 111);
  2585. }
  2586. InbvSetProgressBarSubset(0, 100);
  2587. InbvSetProgressBarCoordinates(303,282);
  2588. } else {
  2589. InbvResetDisplay(); // required to reset display
  2590. }
  2591. StartCount = HIBER_GET_TICK_COUNT(NULL);
  2592. Status = IoInitializeDumpStack (HiberContext->DumpStack, NULL);
  2593. HiberContext->PerfInfo.InitTicks += HIBER_GET_TICK_COUNT(NULL) - StartCount;
  2594. if (!NT_SUCCESS(Status)) {
  2595. PoPrint (PO_HIBERNATE, ("PopSave: dump driver initialization failed %08x\n", Status));
  2596. return Status;
  2597. }
  2598. }
  2599. PERFINFO_HIBER_PAUSE_LOGGING();
  2600. // **************************************
  2601. // FROM HERE OUT NO MEMORY CAN BE EDITED
  2602. // **************************************
  2603. PoHiberInProgress = TRUE;
  2604. //
  2605. // From here out no memory can be edited until the system wakes up, unless
  2606. // that memory has been explicitly accounted for. The list of memory which
  2607. // is allowed to be edited is:
  2608. //
  2609. // - the local stack on each processor
  2610. // - the kernel debuggers global data
  2611. // - the page containing the 16 PTEs used by MM for MmMapMemoryDumpMdl
  2612. // - the restoration table pages
  2613. // - the page containing the MemImage structure
  2614. // - the page containing IoPage
  2615. //
  2616. //
  2617. // Clone required pages
  2618. // (note the MemImage srtucture present at system wake will
  2619. // be the one cloned here)
  2620. //
  2621. Link = HiberContext->ClonedRanges.Flink;
  2622. while (Link != &HiberContext->ClonedRanges) {
  2623. Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
  2624. Link = Link->Flink;
  2625. ASSERT(Range->CloneVa);
  2626. cp = Range->CloneVa;
  2627. sp = Range->StartPage;
  2628. ep = Range->EndPage;
  2629. Mdl = (PMDL) DumpMdl;
  2630. while (sp < ep) {
  2631. PopCreateDumpMdl (Mdl, sp, ep);
  2632. memcpy (cp, Mdl->MappedSystemVa, Mdl->ByteCount);
  2633. cp += Mdl->ByteCount;
  2634. sp += Mdl->ByteCount >> PAGE_SHIFT;
  2635. }
  2636. }
  2637. //
  2638. // Assign page numbers to ranges
  2639. //
  2640. // N.B. We do this here to basically prove that it can be done
  2641. // and to gather some statistics. With the addition of compression,
  2642. // the PageNo field of the Table entries is only applicable to the
  2643. // table pages since uncertain compression ratios do not allow us to
  2644. // predict where each memory range will be written.
  2645. //
  2646. TablePage = &MemImage->FirstTablePage;
  2647. Table = HiberContext->TableHead;
  2648. PageNo = PO_FIRST_RANGE_TABLE_PAGE;
  2649. while (Table) {
  2650. *TablePage = PageNo;
  2651. PageNo += 1;
  2652. for (Index=1; Index <= Table[0].Link.EntryCount; Index++) {
  2653. Table[Index].Range.PageNo = PageNo;
  2654. Pages = Table[Index].Range.EndPage - Table[Index].Range.StartPage;
  2655. PageNo += Pages;
  2656. MemImage->TotalPages += Pages;
  2657. }
  2658. TablePage = &Table[0].Link.NextTable;
  2659. Table = Table[0].Link.Next;
  2660. }
  2661. MemImage->LastFilePage = PageNo;
  2662. PoPrint (PO_HIBERNATE, ("PopSave: NoFree pages %08x\n", MemImage->NoFreePages));
  2663. PoPrint (PO_HIBERNATE, ("PopSave: Memory pages %08x (%dMB)\n", MemImage->TotalPages, MemImage->TotalPages/(PAGE_SIZE/16)));
  2664. PoPrint (PO_HIBERNATE, ("PopSave: File pages %08x (%dMB)\n", MemImage->LastFilePage, MemImage->LastFilePage/(PAGE_SIZE/16)));
  2665. PoPrint (PO_HIBERNATE, ("PopSave: HiberPte %08x for %x\n", MemImage->HiberVa, MemImage->NoHiberPtes));
  2666. //
  2667. // File should be large enough, but check
  2668. //
  2669. if (HiberContext->WriteToFile && PageNo > PopHiberFile.FilePages) {
  2670. PoPrint (PO_HIBERNATE, ("PopSave: File too small - need %x\n", PageNo));
  2671. return STATUS_DISK_FULL;
  2672. }
  2673. //
  2674. // Write the hiberfile image
  2675. //
  2676. Status = PopWriteHiberImage (HiberContext, MemImage, &PopHiberFile);
  2677. PERFINFO_HIBER_DUMP_PERF_BUFFER();
  2678. if (!NT_SUCCESS(Status)) {
  2679. return Status;
  2680. }
  2681. //
  2682. // If debugging, do it again into a second image
  2683. //
  2684. if (PopSimulate & POP_DEBUG_HIBER_FILE) {
  2685. Status = PopWriteHiberImage (HiberContext, MemImage, &PopHiberFileDebug);
  2686. }
  2687. return Status;
  2688. }
  2689. NTSTATUS
  2690. PopWriteHiberImage (
  2691. IN PPOP_HIBER_CONTEXT HiberContext,
  2692. IN PPO_MEMORY_IMAGE MemImage,
  2693. IN PPOP_HIBER_FILE HiberFile
  2694. )
  2695. {
  2696. PPOP_MCB_CONTEXT CMcb;
  2697. PPOP_MEMORY_RANGE Range;
  2698. PPO_MEMORY_RANGE_ARRAY Table;
  2699. ULONG Index;
  2700. PFN_NUMBER sp, ep;
  2701. DUMP_MDL DumpMdl;
  2702. PMDL Mdl;
  2703. PUCHAR cp;
  2704. PFN_NUMBER PageNo;
  2705. PFN_NUMBER Pages;
  2706. PVOID IoPage;
  2707. NTSTATUS Status;
  2708. PPFN_NUMBER TablePage;
  2709. ULONG LastPercent;
  2710. ULONG i;
  2711. ULONG temp;
  2712. ULONG_PTR CompressedWriteOffset = 0;
  2713. PVOID CloneVa;
  2714. ULONG PoWakeCheck;
  2715. LONGLONG EndCount;
  2716. LARGE_INTEGER TickFrequency;
  2717. HiberContext->PerfInfo.StartCount = HIBER_GET_TICK_COUNT(&TickFrequency);
  2718. //
  2719. // Set the sector locations for the proper file
  2720. //
  2721. CMcb = (PPOP_MCB_CONTEXT) HiberContext->CurrentMcb;
  2722. CMcb->FirstMcb = HiberFile->NonPagedMcb;
  2723. CMcb->Mcb = HiberFile->NonPagedMcb;
  2724. CMcb->Base = 0;
  2725. IoPage = HiberContext->IoPage;
  2726. //
  2727. // Write the free page map page
  2728. //
  2729. RtlZeroMemory (IoPage, PAGE_SIZE);
  2730. if (HiberContext->LoaderMdl) {
  2731. //
  2732. // The hibernation file has one page to hold the free page map.
  2733. // If MmHiberPages is more pages than would fit, it's not possible
  2734. // to pass enough free pages to guarantee being able to reload the
  2735. // hibernation image, so don't hibernate.
  2736. //
  2737. if (MmHiberPages > PAGE_SIZE / sizeof (ULONG)) {
  2738. return STATUS_NO_MEMORY;
  2739. }
  2740. MemImage->NoFreePages = HiberContext->LoaderMdl->ByteCount >> PAGE_SHIFT;
  2741. //
  2742. // Hibernate only if the number of free pages on the MDL is more than
  2743. // the required MmHiberPages.
  2744. //
  2745. if (MemImage->NoFreePages >= MmHiberPages) {
  2746. cp = (PUCHAR) MmGetMdlPfnArray( HiberContext->LoaderMdl );
  2747. memcpy (IoPage, cp, MmHiberPages * sizeof(PFN_NUMBER));
  2748. MemImage->NoFreePages = MmHiberPages;
  2749. } else {
  2750. return STATUS_NO_MEMORY;
  2751. }
  2752. } else {
  2753. //
  2754. // If there are no free pages available to pass to the loader, don't
  2755. // hibernate.
  2756. return STATUS_NO_MEMORY;
  2757. }
  2758. MemImage->FreeMapCheck = PoSimpleCheck(0, IoPage, PAGE_SIZE);
  2759. PopWriteHiberPages (HiberContext, IoPage, 1, PO_FREE_MAP_PAGE, NULL);
  2760. //
  2761. // Write the processors saved context
  2762. //
  2763. RtlZeroMemory (IoPage, PAGE_SIZE);
  2764. memcpy (IoPage, HiberContext->WakeState, sizeof(KPROCESSOR_STATE));
  2765. PoWakeCheck =
  2766. MemImage->WakeCheck = PoSimpleCheck(0, IoPage, sizeof(KPROCESSOR_STATE));
  2767. PopWriteHiberPages (HiberContext, IoPage, 1, PO_PROCESSOR_CONTEXT_PAGE, NULL);
  2768. temp = PoSimpleCheck(0, IoPage, sizeof(KPROCESSOR_STATE));
  2769. if (MemImage->WakeCheck != temp) {
  2770. DbgPrint("Checksum for context page changed from %lx to %lx\n",
  2771. MemImage->WakeCheck, temp);
  2772. KeBugCheckEx(INTERNAL_POWER_ERROR, 3, MemImage->WakeCheck, temp, __LINE__);
  2773. }
  2774. temp = PoSimpleCheck(0, IoPage, PAGE_SIZE);
  2775. if (MemImage->WakeCheck != temp) {
  2776. DbgPrint("Checksum for partial context page %lx doesn't match full %lx\n",
  2777. MemImage->WakeCheck, temp);
  2778. KeBugCheckEx(INTERNAL_POWER_ERROR, 4, MemImage->WakeCheck, temp, __LINE__);
  2779. }
  2780. //
  2781. // Before computing checksums, remove all breakpoints so they are not
  2782. // written in the saved image
  2783. //
  2784. if (KdDebuggerEnabled &&
  2785. !KdPitchDebugger &&
  2786. !(PopSimulate & POP_IGNORE_HIBER_SYMBOL_UNLOAD)) {
  2787. KdDeleteAllBreakpoints();
  2788. }
  2789. //
  2790. // Run each range, put its checksum in the restoration table
  2791. // and write each range to the file
  2792. //
  2793. Table = HiberContext->TableHead;
  2794. LastPercent = 100;
  2795. HiberContext->PerfInfo.PagesProcessed = 0;
  2796. TablePage = &MemImage->FirstTablePage;
  2797. PageNo = PO_FIRST_RANGE_TABLE_PAGE;
  2798. PopResetRangeEnum(HiberContext);
  2799. while (Table) {
  2800. // Keep track of where the page tables have been written
  2801. *TablePage = PageNo;
  2802. PageNo++;
  2803. for (Index=1; Index <= Table[0].Link.EntryCount; Index++) {
  2804. PopIOResume (HiberContext, FALSE);
  2805. PopGetNextRange(HiberContext, &sp, &ep, &CloneVa);
  2806. if ((Table[Index].Range.StartPage != sp) ||
  2807. (Table[Index].Range.EndPage != ep)) {
  2808. PoPrint(PO_ERROR,("PopWriteHiberImage: Table entry %p [%lx-%lx] does not match next range [%lx-%lx]\n",
  2809. Table+Index,
  2810. Table[Index].Range.StartPage,
  2811. Table[Index].Range.EndPage,
  2812. sp,
  2813. ep));
  2814. PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
  2815. PopInternalAddToDumpFile( Table, PAGE_SIZE, NULL, NULL, NULL, NULL );
  2816. KeBugCheckEx( INTERNAL_POWER_ERROR,
  2817. 0x107,
  2818. POP_HIBER,
  2819. (ULONG_PTR)HiberContext,
  2820. (ULONG_PTR)Table );
  2821. }
  2822. Table[Index].Range.PageNo = PageNo;
  2823. //
  2824. // Write the data to hiber file
  2825. //
  2826. if (CloneVa) {
  2827. //
  2828. // Use the cloned data which is already mapped
  2829. //
  2830. Pages = ep - sp;
  2831. // Compute the cloned range's Checksum
  2832. Table[Index].Range.CheckSum = 0;
  2833. // Add the pages to the compressed page set
  2834. // (effectively writing them out)
  2835. PopAddPagesToCompressedPageSet(TRUE,
  2836. HiberContext,
  2837. &CompressedWriteOffset,
  2838. CloneVa,
  2839. Pages,
  2840. &PageNo);
  2841. HiberContext->PerfInfo.PagesProcessed += (ULONG)Pages;
  2842. // Update the progress bar
  2843. i = (ULONG)((HiberContext->PerfInfo.PagesProcessed * 100) / MemImage->TotalPages);
  2844. if (i != LastPercent) {
  2845. LastPercent = i;
  2846. PopUpdateHiberComplete(HiberContext, LastPercent);
  2847. }
  2848. } else {
  2849. //
  2850. // Map a chunk and write it, loop until done
  2851. //
  2852. Mdl = (PMDL) DumpMdl;
  2853. // Initialize Check Sum
  2854. Table[Index].Range.CheckSum = 0;
  2855. while (sp < ep) {
  2856. PopCreateDumpMdl (Mdl, sp, ep);
  2857. Pages = Mdl->ByteCount >> PAGE_SHIFT;
  2858. // Add pages to compressed page set
  2859. // (effectively writing them out)
  2860. PopAddPagesToCompressedPageSet(TRUE,
  2861. HiberContext,
  2862. &CompressedWriteOffset,
  2863. Mdl->MappedSystemVa,
  2864. Pages,
  2865. &PageNo);
  2866. sp += Pages;
  2867. HiberContext->PerfInfo.PagesProcessed += (ULONG)Pages;
  2868. // Update the progress bar
  2869. i = (ULONG)((HiberContext->PerfInfo.PagesProcessed * 100) / MemImage->TotalPages);
  2870. if (i != LastPercent) {
  2871. LastPercent = i;
  2872. PopUpdateHiberComplete(HiberContext, LastPercent);
  2873. }
  2874. }
  2875. }
  2876. }
  2877. // Terminate the compressed page set, since the next page
  2878. // (a table page) is uncompressed.
  2879. PopEndCompressedPageSet(HiberContext, &CompressedWriteOffset, &PageNo);
  2880. TablePage = &Table[0].Link.NextTable;
  2881. Table = Table[0].Link.Next;
  2882. }
  2883. //
  2884. // Now that the range checksums have been added to the
  2885. // restoration tables they are now complete. Compute their
  2886. // checksums and write them into the file
  2887. //
  2888. Table = HiberContext->TableHead;
  2889. PageNo = PO_FIRST_RANGE_TABLE_PAGE;
  2890. while (Table) {
  2891. Table[0].Link.CheckSum = 0;
  2892. PopWriteHiberPages (HiberContext, Table, 1, PageNo, NULL);
  2893. PageNo = Table[0].Link.NextTable;
  2894. Table = Table[0].Link.Next;
  2895. }
  2896. //
  2897. // File is complete write a valid header
  2898. //
  2899. if (MemImage->WakeCheck != PoWakeCheck) {
  2900. DbgPrint("MemImage->WakeCheck %lx doesn't make PoWakeCheck %lx\n",
  2901. MemImage->WakeCheck,
  2902. PoWakeCheck);
  2903. KeBugCheckEx(INTERNAL_POWER_ERROR, 5, MemImage->WakeCheck, PoWakeCheck, __LINE__);
  2904. }
  2905. //
  2906. // Fill in perf information so we can read it after hibernation
  2907. //
  2908. EndCount = HIBER_GET_TICK_COUNT(&TickFrequency);
  2909. HiberContext->PerfInfo.ElapsedTime = (ULONG)((EndCount - HiberContext->PerfInfo.StartCount)*1000 / TickFrequency.QuadPart);
  2910. HiberContext->PerfInfo.IoTime = (ULONG)(HiberContext->PerfInfo.IoTicks*1000 / TickFrequency.QuadPart);
  2911. HiberContext->PerfInfo.CopyTime = (ULONG)(HiberContext->PerfInfo.CopyTicks*1000 / TickFrequency.QuadPart);
  2912. HiberContext->PerfInfo.InitTime = (ULONG)(HiberContext->PerfInfo.InitTicks*1000 / TickFrequency.QuadPart);
  2913. HiberContext->PerfInfo.FileRuns = PopHiberFile.McbSize / sizeof(LARGE_INTEGER) - 1;
  2914. MemImage->Signature = PO_IMAGE_SIGNATURE;
  2915. MemImage->PerfInfo = HiberContext->PerfInfo;
  2916. MemImage->CheckSum = PoSimpleCheck(0, MemImage, sizeof(*MemImage));
  2917. PopWriteHiberPages (HiberContext, MemImage, 1, PO_IMAGE_HEADER_PAGE, NULL);
  2918. //
  2919. // Image completely written flush the controller
  2920. //
  2921. if (HiberContext->WriteToFile) {
  2922. while (NT_SUCCESS (HiberContext->Status) &&
  2923. (DmaIoPtr != NULL) &&
  2924. ((DmaIoPtr->Busy.Size != 0) || (DmaIoPtr->Used.Size != 0))) {
  2925. PopIOResume (HiberContext, TRUE);
  2926. }
  2927. HiberContext->DumpStack->Init.FinishRoutine();
  2928. }
  2929. if (PopSimulate & POP_ENABLE_HIBER_PERF) {
  2930. PopDumpStatistics(&HiberContext->PerfInfo);
  2931. }
  2932. //
  2933. // Failed to write the hiberfile.
  2934. //
  2935. if ( !NT_SUCCESS(HiberContext->Status) || (PopSimulate & POP_FORCE_HIBERNATE_FAILURE) ) {
  2936. #if DBG
  2937. PoPrint (PO_ERROR, ("PopWriteHiberImage: Error occured writing the hiberfile. (%x)\n", HiberContext->Status));
  2938. PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
  2939. KeBugCheckEx( INTERNAL_POWER_ERROR,
  2940. 0x108,
  2941. POP_HIBER,
  2942. (ULONG_PTR)HiberContext,
  2943. 0 );
  2944. #else
  2945. return( (NT_SUCCESS(HiberContext->Status) && (PopSimulate & POP_FORCE_HIBERNATE_FAILURE)) ? STATUS_UNSUCCESSFUL : (HiberContext->Status) );
  2946. #endif
  2947. }
  2948. //
  2949. // Before sleeping, if the check memory bit is set verify the
  2950. // dump process didn't edit any memory pages
  2951. //
  2952. if (PopSimulate & POP_TEST_CRC_MEMORY) {
  2953. if (!(PopSimulate & POP_DEBUG_HIBER_FILE) ||
  2954. (HiberFile == &PopHiberFileDebug)) {
  2955. }
  2956. }
  2957. //
  2958. // Tell the debugger we are hibernating
  2959. //
  2960. if (!(PopSimulate & POP_IGNORE_HIBER_SYMBOL_UNLOAD)) {
  2961. KD_SYMBOLS_INFO SymbolInfo = {0};
  2962. SymbolInfo.BaseOfDll = (PVOID)KD_HIBERNATE;
  2963. DebugService2(NULL, &SymbolInfo, BREAKPOINT_UNLOAD_SYMBOLS);
  2964. }
  2965. //
  2966. // If we want to perform a reset instead of a power down, return an
  2967. // error so we don't power down
  2968. //
  2969. if (PopSimulate & POP_RESET_ON_HIBER) {
  2970. return STATUS_DEVICE_DOES_NOT_EXIST;
  2971. }
  2972. //
  2973. // Success, continue with power off operation
  2974. //
  2975. return STATUS_SUCCESS;
  2976. }
  2977. VOID
  2978. PopDumpStatistics(
  2979. IN PPO_HIBER_PERF PerfInfo
  2980. )
  2981. {
  2982. LONGLONG EndCount;
  2983. LARGE_INTEGER TickFrequency;
  2984. EndCount = HIBER_GET_TICK_COUNT(&TickFrequency);
  2985. PerfInfo->ElapsedTime = (ULONG)((EndCount - PerfInfo->StartCount)*1000 / TickFrequency.QuadPart);
  2986. PerfInfo->IoTime = (ULONG)(PerfInfo->IoTicks*1000 / TickFrequency.QuadPart);
  2987. PerfInfo->CopyTime = (ULONG)(PerfInfo->CopyTicks*1000 / TickFrequency.QuadPart);
  2988. PerfInfo->InitTime = (ULONG)(PerfInfo->InitTicks*1000 / TickFrequency.QuadPart);
  2989. PerfInfo->FileRuns = PopHiberFile.McbSize / sizeof(LARGE_INTEGER) - 1;
  2990. DbgPrint("HIBER: %lu Pages written in %lu Dumps (%lu runs).\n",
  2991. PerfInfo->PagesWritten,
  2992. PerfInfo->DumpCount,
  2993. PerfInfo->FileRuns);
  2994. DbgPrint("HIBER: %lu Pages processed (%d %% compression)\n",
  2995. PerfInfo->PagesProcessed,
  2996. PerfInfo->PagesWritten*100/PerfInfo->PagesProcessed);
  2997. DbgPrint("HIBER: Elapsed time %3d.%03d seconds\n",
  2998. PerfInfo->ElapsedTime / 1000,
  2999. PerfInfo->ElapsedTime % 1000);
  3000. DbgPrint("HIBER: I/O time %3d.%03d seconds (%2d%%) %d MB/sec\n",
  3001. PerfInfo->IoTime / 1000,
  3002. PerfInfo->IoTime % 1000,
  3003. PerfInfo->ElapsedTime ? PerfInfo->IoTime*100/PerfInfo->ElapsedTime : 0,
  3004. (PerfInfo->IoTime/100000) ? (PerfInfo->PagesWritten/(1024*1024/PAGE_SIZE)) / (PerfInfo->IoTime / 100000) : 0);
  3005. DbgPrint("HIBER: Init time %3d.%03d seconds (%2d%%)\n",
  3006. PerfInfo->InitTime / 1000,
  3007. PerfInfo->InitTime % 1000,
  3008. PerfInfo->ElapsedTime ? PerfInfo->InitTime*100/PerfInfo->ElapsedTime : 0);
  3009. DbgPrint("HIBER: Copy time %3d.%03d seconds (%2d%%) %d Bytes\n",
  3010. PerfInfo->CopyTime / 1000,
  3011. PerfInfo->CopyTime % 1000,
  3012. PerfInfo->ElapsedTime ? PerfInfo->CopyTime*100/PerfInfo->ElapsedTime : 0,
  3013. PerfInfo->BytesCopied );
  3014. }
  3015. VOID
  3016. PopUpdateHiberComplete (
  3017. IN PPOP_HIBER_CONTEXT HiberContext,
  3018. IN ULONG Percent
  3019. )
  3020. {
  3021. UCHAR Buffer[200];
  3022. if (InbvIsBootDriverInstalled()) {
  3023. InbvUpdateProgressBar(Percent + 1);
  3024. } else {
  3025. sprintf (Buffer, "PopSave: %d%%\r", Percent);
  3026. PoPrint (PO_HIBER_MAP, ("%s", Buffer));
  3027. if (HiberContext->WriteToFile) {
  3028. InbvDisplayString (Buffer);
  3029. }
  3030. }
  3031. #if 0
  3032. if ((Percent > 0) &&
  3033. ((Percent % 10) == 0) &&
  3034. (PopSimulate & POP_ENABLE_HIBER_PERF)) {
  3035. DbgPrint("HIBER: %d %% done\n",Percent);
  3036. PopDumpStatistics(&HiberContext->PerfInfo);
  3037. }
  3038. #endif
  3039. }
  3040. VOID
  3041. PopEndCompressedPageSet(
  3042. IN PPOP_HIBER_CONTEXT HiberContext,
  3043. IN OUT PULONG_PTR CompressedBufferOffset,
  3044. IN OUT PPFN_NUMBER SetFilePage
  3045. )
  3046. /*++
  3047. Routine Description:
  3048. Terminates a compressed page set, flushing whatever remains in the compression
  3049. buffer to the Hiber file. A termination of a compressed page set allows uncompressed
  3050. pages to the be written out to the Hiber file.
  3051. See PopAddPagesToCompressedPageSet for more information on compressed page sets.
  3052. Arguments:
  3053. HiberContext - The Hiber Context.
  3054. CompressedBufferOffset - Similar to same parameter in PopAddPagesToCompressedPageSet.
  3055. Should be the CompressedBufferOffset value received
  3056. from the last call to PopAddPagesToCompressedPageSet.
  3057. Will be reset to 0 after this call in preparation
  3058. for the beginning of a new compressed page set.
  3059. SetFilePage - Similar to same parameter in PopAddPagsToCompressedPageSet.
  3060. Should be the SetFilePAge value received from the last
  3061. call to PopAddPagesToCompressedPageSet. Will be reset
  3062. to the next available file page after the end of this
  3063. compressed page set.
  3064. Return Value:
  3065. None.
  3066. --*/
  3067. {
  3068. PFN_NUMBER Pages;
  3069. PCOMPRESSION_BLOCK Block = HiberContext->CompressionBlock;
  3070. // is there are any blocked data?
  3071. if (Block->Ptr != Block->Buffer) {
  3072. // yes, flush the block
  3073. PopAddPagesToCompressedPageSet (FALSE, // no buffering -- compress now
  3074. HiberContext,
  3075. CompressedBufferOffset,
  3076. Block->Buffer,
  3077. (PFN_NUMBER) ((Block->Ptr - Block->Buffer) >> PAGE_SHIFT),
  3078. SetFilePage);
  3079. // reset block to empty
  3080. Block->Ptr = Block->Buffer;
  3081. }
  3082. // Figure out how many pages remain in the compression buffer. Don't
  3083. // use BYTES_TO_PAGES because that will truncate to ULONG.
  3084. Pages = (PFN_NUMBER) ((*CompressedBufferOffset + (PAGE_SIZE-1)) >> PAGE_SHIFT);
  3085. if (Pages > 0) {
  3086. // Write the remaining pages out
  3087. PopWriteHiberPages(HiberContext,
  3088. (PVOID)HiberContext->CompressedWriteBuffer,
  3089. Pages,
  3090. *SetFilePage,
  3091. NULL);
  3092. // Reflect our usage of the hiber file
  3093. *SetFilePage = *SetFilePage + Pages;
  3094. }
  3095. *CompressedBufferOffset = 0;
  3096. }
  3097. VOID
  3098. PopAddPagesToCompressedPageSet(
  3099. IN BOOLEAN AllowDataBuffering,
  3100. IN PPOP_HIBER_CONTEXT HiberContext,
  3101. IN OUT PULONG_PTR CompressedBufferOffset,
  3102. IN PVOID StartVa,
  3103. IN PFN_NUMBER NumPages,
  3104. IN OUT PPFN_NUMBER SetFilePage
  3105. )
  3106. /*++
  3107. Routine Description:
  3108. This routine is the central call needed to write out memory pages
  3109. in a compressed fashion.
  3110. This routine takes a continuous range of mapped pages and adds
  3111. them to a compressed page set. A compressed page set is merely
  3112. a stream of compressed buffers written out contiguously within
  3113. the Hiber file. Such a contiguous layout maximizes the benefit
  3114. gained from compression by writing compressed output into
  3115. the smallest possible space.
  3116. In order to accomplish such a layout, this routine continually
  3117. compresses pages and adds them to the compression buffer pointed to
  3118. by the Hiber context. Once a certain point in that buffer is reached,
  3119. it is written out to the Hiber file and the buffer is reset to the
  3120. beginning. Each write-out of the compression buffer is placed
  3121. right after the end of the last compression buffer written.
  3122. Because of the buffering used in this algorithm, compressed buffers
  3123. may remain in the compression buffer even after the last needed
  3124. call to PopAddPagesToCompressedPageSet. In order to fully flush
  3125. the buffer, PopEndCompressedPageSet must be called.
  3126. Note that in order to write any uncompressed pages to the Hiber
  3127. file, the compressed page set needs to be terminated with
  3128. PopEndCompressedPageSet. After a compressed page set is terminated,
  3129. a new set can be initiated with a call to PopAddPagesToCompressedPageSet.
  3130. N.B. A chunk of a compressed page set that has been committed to the
  3131. Hiber file in one write operation is called a compressed page set fragment
  3132. in other places within this file.
  3133. Arguments:
  3134. AllowDataBuffering - If true input pages will be buffered, otherwise
  3135. - compressed and [possibly] written immediately
  3136. HiberContext - The Hiber context
  3137. CompressedBufferOffset - An offset into the Hiber context's compression buffer
  3138. where the addition of the next compressed buffer will
  3139. occurr.
  3140. This offset should be set to 0 at the beginning of
  3141. every compressed page set. After every call,
  3142. to PopAddPagesToCompressedPageSet this offset
  3143. will be modified to reflect the current usage of
  3144. the compression buffer.
  3145. StartVa - The starting virtual address of the pages to
  3146. add to the compressed page set.
  3147. NumPages - The number of pages to add to the compressed page set.
  3148. SetFilePage - A pointer to first page in the Hiber file that will receive
  3149. the next write-out of the compression buffer.
  3150. This page should be set to the first available Hiber file
  3151. page when the compressed page set is begun. The page will
  3152. be reset to reflect the current usage of the Hiber file
  3153. by the compressed page set after each call to
  3154. PopAddPagesToCompressedPageSet.
  3155. Return Value:
  3156. NONE.
  3157. --*/
  3158. {
  3159. ULONG_PTR BufferOffset = *CompressedBufferOffset;
  3160. PUCHAR Page = (PUCHAR)StartVa;
  3161. PFN_NUMBER i;
  3162. ULONG CompressedSize;
  3163. PFN_NUMBER NumberOfPagesToCompress;
  3164. ULONG MaxCompressedSize;
  3165. ULONG AlignedCompressedSize;
  3166. PUCHAR CompressedBuffer;
  3167. if (AllowDataBuffering) {
  3168. PCOMPRESSION_BLOCK Block = HiberContext->CompressionBlock;
  3169. // Yes, try to buffer output
  3170. if (Block->Ptr != Block->Buffer) {
  3171. // Find # of free pages left in block
  3172. NumberOfPagesToCompress = (PFN_NUMBER)
  3173. ((Block->Buffer + sizeof (Block->Buffer) - Block->Ptr) >> PAGE_SHIFT);
  3174. // If it's exceed available truncate
  3175. if (NumberOfPagesToCompress > NumPages) {
  3176. NumberOfPagesToCompress = NumPages;
  3177. }
  3178. // Any free space left?
  3179. if (NumberOfPagesToCompress != 0) {
  3180. HbCopy(HiberContext, Block->Ptr, Page, NumberOfPagesToCompress << PAGE_SHIFT);
  3181. NumPages -= NumberOfPagesToCompress;
  3182. Page += NumberOfPagesToCompress << PAGE_SHIFT;
  3183. Block->Ptr += NumberOfPagesToCompress << PAGE_SHIFT;
  3184. }
  3185. // Is block full?
  3186. if (Block->Ptr == Block->Buffer + sizeof (Block->Buffer)) {
  3187. // Yes, flush the block
  3188. PopAddPagesToCompressedPageSet (FALSE, // no buffering
  3189. HiberContext,
  3190. CompressedBufferOffset,
  3191. Block->Buffer,
  3192. (PFN_NUMBER) ((Block->Ptr - Block->Buffer) >> PAGE_SHIFT),
  3193. SetFilePage);
  3194. // Reset block to empty
  3195. Block->Ptr = Block->Buffer;
  3196. }
  3197. }
  3198. NumberOfPagesToCompress = sizeof (Block->Buffer) >> PAGE_SHIFT;
  3199. // While too much to compress -- compress from original location
  3200. while (NumPages >= NumberOfPagesToCompress) {
  3201. // Write pages
  3202. PopAddPagesToCompressedPageSet (FALSE, // no buffering
  3203. HiberContext,
  3204. CompressedBufferOffset,
  3205. Page,
  3206. NumberOfPagesToCompress,
  3207. SetFilePage);
  3208. // adjust pointer and counter
  3209. Page += NumberOfPagesToCompress << PAGE_SHIFT;
  3210. NumPages -= NumberOfPagesToCompress;
  3211. }
  3212. // If anything left save it in block
  3213. // N.B.: either NumPages == 0 or there is enough space in Block
  3214. if (NumPages != 0) {
  3215. HbCopy (HiberContext, Block->Ptr, Page, NumPages << PAGE_SHIFT);
  3216. Block->Ptr += NumPages << PAGE_SHIFT;
  3217. }
  3218. // done
  3219. return;
  3220. }
  3221. // First make sure values of constants match our assumptions
  3222. #if XPRESS_HEADER_SIZE < XPRESS_HEADER_STRING_SIZE + 8
  3223. #error -- XPRESS_HEADER_SIZE shall be at least (XPRESS_HEADER_STRING_SIZE + 8)
  3224. #endif
  3225. #if XPRESS_MAX_SIZE < PAGE_SIZE || XPRESS_MAX_SIZE % PAGE_SIZE != 0
  3226. #error -- XPRESS_MAX_SIZE shall be multiple of PAGE_SIZE
  3227. #endif
  3228. #if (XPRESS_ALIGNMENT & (XPRESS_ALIGNMENT - 1)) != 0
  3229. #error -- XPRESS_ALIGNMENT shall be power of 2
  3230. #endif
  3231. #if XPRESS_HEADER_SIZE % XPRESS_ALIGNMENT != 0
  3232. #error -- XPRESS_HEADER_SIZE shall be multiple of XPRESS_ALIGNMENT
  3233. #endif
  3234. // make sure that compressed buffer and its header will fit into output buffer
  3235. #if XPRESS_MAX_SIZE + XPRESS_HEADER + PAGE_SIZE - 1 > (POP_COMPRESSED_PAGE_SET_SIZE << PAGE_SHIFT)
  3236. #error -- POP_COMPRESSED_PAGE_SET_SIZE is too small
  3237. #endif
  3238. // Real compression starts here
  3239. // Loop through all the pages ...
  3240. for (i = 0; i < NumPages; i += NumberOfPagesToCompress) {
  3241. NumberOfPagesToCompress = XPRESS_MAX_PAGES;
  3242. if (NumberOfPagesToCompress > NumPages - i) {
  3243. NumberOfPagesToCompress = NumPages - i;
  3244. }
  3245. // If compressed data occupies more than 87.5% = 7/8 of original store data as is
  3246. MaxCompressedSize = ((ULONG)NumberOfPagesToCompress * 7) * (PAGE_SIZE / 8);
  3247. // Is the buffer use beyond the write-out threshold?
  3248. //
  3249. // N.B. The buffer must extend sufficiently beyond the threshold
  3250. // the allow the last compression operation (that one that writes
  3251. // beyond the threshold) to always succeed.
  3252. //
  3253. if (BufferOffset + (NumberOfPagesToCompress << PAGE_SHIFT) + XPRESS_HEADER_SIZE > (POP_COMPRESSED_PAGE_SET_SIZE << PAGE_SHIFT)) {
  3254. // Write out the compression buffer bytes below the threshold
  3255. PopWriteHiberPages(HiberContext,
  3256. (PVOID)HiberContext->CompressedWriteBuffer,
  3257. BufferOffset >> PAGE_SHIFT,
  3258. *SetFilePage,
  3259. NULL);
  3260. // We have used some pages in the Hiber file with the above write,
  3261. // indicate that our next Hiber file page will be beyond those used pages.
  3262. *SetFilePage = *SetFilePage + (BufferOffset >> PAGE_SHIFT);
  3263. // Move buffer bytes that are above the write-out threshold to the
  3264. // beginning of the buffer
  3265. if (BufferOffset & (PAGE_SIZE - 1)) {
  3266. HbCopy(HiberContext,
  3267. HiberContext->CompressedWriteBuffer,
  3268. HiberContext->CompressedWriteBuffer + (BufferOffset & ~(PAGE_SIZE - 1)),
  3269. (ULONG)BufferOffset & (PAGE_SIZE - 1));
  3270. }
  3271. // Reset the buffer offset back to the beginning of the buffer but right
  3272. // after any above-threshold buffer bytes that we will move to the beginning
  3273. // of the buffer
  3274. BufferOffset &= PAGE_SIZE - 1;
  3275. }
  3276. // Remember output position
  3277. CompressedBuffer = HiberContext->CompressedWriteBuffer + BufferOffset;
  3278. // Clear the header
  3279. RtlZeroMemory (CompressedBuffer, XPRESS_HEADER_SIZE);
  3280. // Compress pages into the compression buffer
  3281. if (HIBER_USE_DMA (HiberContext)) {
  3282. // Try to resume IO calling callback each 8192 bytes
  3283. CompressedSize = XpressEncode ((XpressEncodeStream) (HiberContext->CompressionWorkspace),
  3284. CompressedBuffer + XPRESS_HEADER_SIZE,
  3285. MaxCompressedSize,
  3286. (PVOID) Page,
  3287. (ULONG)NumberOfPagesToCompress << PAGE_SHIFT,
  3288. PopIOCallback,
  3289. HiberContext,
  3290. 8192);
  3291. } else {
  3292. // No need for callbacks -- compress everything at once
  3293. CompressedSize = XpressEncode ((XpressEncodeStream) (HiberContext->CompressionWorkspace),
  3294. CompressedBuffer + XPRESS_HEADER_SIZE,
  3295. MaxCompressedSize,
  3296. (PVOID) Page,
  3297. (ULONG)NumberOfPagesToCompress << PAGE_SHIFT,
  3298. NULL,
  3299. NULL,
  3300. 0);
  3301. }
  3302. // If compression failed copy data as is original
  3303. if (CompressedSize >= MaxCompressedSize) {
  3304. CompressedSize = (ULONG)NumberOfPagesToCompress << PAGE_SHIFT;
  3305. HbCopy (HiberContext,
  3306. CompressedBuffer + XPRESS_HEADER_SIZE,
  3307. (PVOID) Page,
  3308. CompressedSize);
  3309. }
  3310. //
  3311. // Fill the header
  3312. //
  3313. // Magic bytes (LZNT1 block cannot start from 0x81,0x81)
  3314. RtlCopyMemory (CompressedBuffer, XPRESS_HEADER_STRING, XPRESS_HEADER_STRING_SIZE);
  3315. // Size of original and compressed data
  3316. {
  3317. ULONG dw = ((CompressedSize - 1) << 10) + ((ULONG)NumberOfPagesToCompress - 1);
  3318. #if XPRESS_MAX_SIZE > (1 << 22)
  3319. #error -- XPRESS_MAX_SIZE shall not exceed 4 MB
  3320. #endif
  3321. CompressedBuffer[XPRESS_HEADER_STRING_SIZE] = (UCHAR) dw;
  3322. CompressedBuffer[XPRESS_HEADER_STRING_SIZE+1] = (UCHAR) (dw >> 8);
  3323. CompressedBuffer[XPRESS_HEADER_STRING_SIZE+2] = (UCHAR) (dw >> 16);
  3324. CompressedBuffer[XPRESS_HEADER_STRING_SIZE+3] = (UCHAR) (dw >> 24);
  3325. }
  3326. // Align compressed data on 8-byte boundary
  3327. AlignedCompressedSize = (CompressedSize + (XPRESS_ALIGNMENT - 1)) & ~(XPRESS_ALIGNMENT - 1);
  3328. if (CompressedSize != AlignedCompressedSize) {
  3329. // Fill up data with zeroes until aligned
  3330. RtlZeroMemory (CompressedBuffer + XPRESS_HEADER_SIZE + CompressedSize, AlignedCompressedSize - CompressedSize);
  3331. }
  3332. // Indicate our new usage of the buffer
  3333. BufferOffset += AlignedCompressedSize + XPRESS_HEADER_SIZE;
  3334. // Move on to the virtual address of the next page
  3335. Page += NumberOfPagesToCompress << PAGE_SHIFT;
  3336. }
  3337. *CompressedBufferOffset = BufferOffset;
  3338. }
  3339. VOID
  3340. PopIORegionMove (
  3341. IN IOREGION *To, // ptr to region descriptor to put bytes to
  3342. IN IOREGION *From, // ptr to region descriptor to get bytes from
  3343. IN LONG Bytes // # of bytes to move from the beginning of one region to the end of another
  3344. )
  3345. {
  3346. ASSERT((Bytes & (PAGE_SIZE-1)) == 0);
  3347. if (To->Size != To->End - To->Ptr) {
  3348. ASSERT (To->Ptr + To->Size == From->Ptr);
  3349. To->Size += Bytes;
  3350. ASSERT (To->Size <= To->End - To->Ptr);
  3351. } else {
  3352. ASSERT (To->Beg + To->SizeOvl == From->Ptr);
  3353. To->SizeOvl += Bytes;
  3354. ASSERT (To->Size + To->SizeOvl <= To->End - To->Beg);
  3355. }
  3356. ASSERT (Bytes <= From->Size && From->Size <= From->End - From->Ptr);
  3357. From->Size -= Bytes;
  3358. From->Ptr += Bytes;
  3359. if (From->Ptr == From->End) {
  3360. ASSERT (From->Size == 0);
  3361. From->Ptr = From->Beg;
  3362. From->Size = From->SizeOvl;
  3363. From->SizeOvl = 0;
  3364. }
  3365. }
  3366. VOID
  3367. XPRESS_CALL
  3368. PopIOCallback (
  3369. PVOID Context,
  3370. int compressed
  3371. )
  3372. {
  3373. PPOP_HIBER_CONTEXT HiberContext = Context;
  3374. if (HiberContext == NULL || DmaIoPtr == NULL) {
  3375. return;
  3376. }
  3377. if (DmaIoPtr->Busy.Size == 0 && DmaIoPtr->Used.Size == 0)
  3378. return;
  3379. PopIOResume (Context, FALSE);
  3380. }
  3381. BOOLEAN PopIOResume (
  3382. IN PPOP_HIBER_CONTEXT HiberContext,
  3383. IN BOOLEAN Complete
  3384. )
  3385. {
  3386. NTSTATUS status;
  3387. // If there were error don't even bother
  3388. if (!NT_SUCCESS(HiberContext->Status)) {
  3389. return(FALSE);
  3390. }
  3391. if (DmaIoPtr == NULL) {
  3392. return(TRUE);
  3393. }
  3394. // if delayed operation then resume or complete it
  3395. while (DmaIoPtr->Busy.Size != 0) {
  3396. status = HiberContext->DumpStack->Init.WritePendingRoutine (Complete?IO_DUMP_WRITE_FINISH:IO_DUMP_WRITE_RESUME,
  3397. NULL,
  3398. NULL,
  3399. DmaIoPtr->DumpLocalData);
  3400. if (status == STATUS_PENDING) {
  3401. // Pending IO; shall never happen if Complete
  3402. ASSERT (!Complete);
  3403. return(TRUE);
  3404. }
  3405. // If there were error then don't care
  3406. if (!NT_SUCCESS (status)) {
  3407. HiberContext->Status = status;
  3408. return(FALSE);
  3409. }
  3410. // Now, resume PopWriteHiberPages
  3411. PopWriteHiberPages (HiberContext,
  3412. NULL,
  3413. 0,
  3414. 0,
  3415. &DmaIoPtr->HiberWritePagesLocals);
  3416. if (!NT_SUCCESS (HiberContext->Status)) {
  3417. return(FALSE);
  3418. }
  3419. // If pending IO completed and we had to wait -- do not start new one
  3420. if (DmaIoPtr->Busy.Size == 0 && Complete) {
  3421. return(TRUE);
  3422. }
  3423. // If not completed and do no wait -- return
  3424. if (DmaIoPtr->Busy.Size != 0 && !Complete) {
  3425. return(TRUE);
  3426. }
  3427. }
  3428. while (DmaIoPtr->Used.Size >= PAGE_SIZE) {
  3429. ULONG_PTR i, j;
  3430. ULONG_PTR NoPages;
  3431. ULONG_PTR Length;
  3432. PUCHAR PageVa;
  3433. PFN_NUMBER FilePage;
  3434. // Obtain size of region waiting for IO
  3435. PageVa = DmaIoPtr->Used.Ptr;
  3436. NoPages = (Length = DmaIoPtr->Used.Size) >> PAGE_SHIFT;
  3437. // Make sure all pages should be contiguous
  3438. i = DmaIoPtr->Used.Ptr - DmaIoPtr->Used.Beg;
  3439. ASSERT (((i | Length) & (PAGE_SIZE-1)) == 0);
  3440. i >>= PAGE_SHIFT;
  3441. // Starting file offset (in pages)
  3442. FilePage = DmaIoPtr->FilePage[i];
  3443. // Increase counter while contiguous and used
  3444. if (HIBER_USE_DMA (HiberContext)) {
  3445. // If DMA is allowed write page-by-page
  3446. j = 1;
  3447. } else {
  3448. // Write as many pages as possible
  3449. j = 0;
  3450. do {
  3451. ++j;
  3452. } while ((j != NoPages) &&
  3453. (DmaIoPtr->FilePage[i + j] == FilePage + j));
  3454. }
  3455. // Re-evaluate # of pages and length of block
  3456. Length = (NoPages = j) << PAGE_SHIFT;
  3457. // Start IO
  3458. PopWriteHiberPages (HiberContext, PageVa, NoPages, FilePage, &DmaIoPtr->HiberWritePagesLocals);
  3459. if (!NT_SUCCESS (HiberContext->Status)) {
  3460. return(FALSE);
  3461. }
  3462. // If pending then return immediately (even if need to complete)
  3463. if (DmaIoPtr->Busy.Size != 0) {
  3464. return(TRUE);
  3465. }
  3466. }
  3467. return(TRUE);
  3468. }
  3469. VOID
  3470. PopIOWrite (
  3471. IN PPOP_HIBER_CONTEXT HiberContext,
  3472. IN PUCHAR Ptr,
  3473. IN LONG Bytes,
  3474. IN PFN_NUMBER FilePage
  3475. )
  3476. {
  3477. LONG i, Size;
  3478. ULONGLONG StartCount;
  3479. // Do not bother if don't writing and/or was an error
  3480. if (!HiberContext->WriteToFile || !NT_SUCCESS(HiberContext->Status)) {
  3481. return;
  3482. }
  3483. ASSERT ((Bytes & (PAGE_SIZE-1)) == 0);
  3484. while (Bytes > 0) {
  3485. // Complete or Resume IO
  3486. do {
  3487. if (!PopIOResume (HiberContext, (BOOLEAN) (DmaIoPtr->Free.Size == 0))) {
  3488. return;
  3489. }
  3490. } while (DmaIoPtr->Free.Size == 0);
  3491. // Find how much can we write
  3492. Size = DmaIoPtr->Free.Size;
  3493. ASSERT ((Size & (PAGE_SIZE-1)) == 0);
  3494. if (Size > Bytes) {
  3495. Size = Bytes;
  3496. }
  3497. ASSERT (Size != 0);
  3498. // Copy and adjust pointers
  3499. HbCopy (HiberContext, DmaIoPtr->Free.Ptr, Ptr, Size);
  3500. Ptr += Size;
  3501. Bytes -= Size;
  3502. // Remember current page # index
  3503. i = (ULONG)(DmaIoPtr->Free.Ptr - DmaIoPtr->Free.Beg);
  3504. ASSERT ((i & (PAGE_SIZE-1)) == 0);
  3505. i >>= PAGE_SHIFT;
  3506. // Mark free memory as used
  3507. PopIORegionMove (&DmaIoPtr->Used, &DmaIoPtr->Free, Size);
  3508. // Remember FilePage for newly used pages
  3509. do {
  3510. DmaIoPtr->FilePage[i] = FilePage;
  3511. ++i;
  3512. ++FilePage;
  3513. } while ((Size -= PAGE_SIZE) != 0);
  3514. }
  3515. // Resume IO
  3516. PopIOResume (HiberContext, FALSE);
  3517. }
  3518. VOID
  3519. PopWriteHiberPages (
  3520. IN PPOP_HIBER_CONTEXT HiberContext,
  3521. IN PVOID ArgPageVa,
  3522. IN PFN_NUMBER ArgNoPages,
  3523. IN PFN_NUMBER ArgFilePage,
  3524. IN HIBER_WRITE_PAGES_LOCALS *Locals
  3525. )
  3526. /*++
  3527. Routine Description:
  3528. Routine to write pages into the hibernation file.
  3529. Caller must map pages to virtual addresses.
  3530. Arguments:
  3531. HiberContext - The hibernation context structure
  3532. PageVa - Virtual address of the first page to write
  3533. NoPage - Number of consective pages to write
  3534. FilePage - Page address in hiber file to write this
  3535. run of pages.
  3536. PendingIOStatus - If NULL then pass IO request to PopIOWrite,
  3537. otherwise it's call from PopIOResume for delayed
  3538. IO; used to return # of bytes written and pending
  3539. Return Value:
  3540. None
  3541. --*/
  3542. {
  3543. DUMP_MDL DumpMdl;
  3544. #define X(type,name) type name
  3545. HIBER_WRITE_PAGES_LOCALS_LIST (X)
  3546. #undef X
  3547. ULONGLONG StartCount, EndCount;
  3548. //
  3549. // Copy arguments to local variables
  3550. //
  3551. PageVa = ArgPageVa;
  3552. NoPages = ArgNoPages;
  3553. FilePage = ArgFilePage;
  3554. //
  3555. // Allow debugger to break in when we are hibernating.
  3556. //
  3557. KdCheckForDebugBreak ();
  3558. //
  3559. // If a file isn't being written, then ignore
  3560. //
  3561. if (!HiberContext->WriteToFile) {
  3562. return ;
  3563. }
  3564. //
  3565. // If there's been some sort of error, don't bother
  3566. // writing anymore
  3567. //
  3568. if (!NT_SUCCESS(HiberContext->Status)) {
  3569. return ;
  3570. }
  3571. Mdl = (PMDL) DumpMdl;
  3572. if (Locals != NULL) {
  3573. // If we have async IO make sure that hand-made MDL will be
  3574. // stored in safe place preserved between resume calls
  3575. Mdl = (PMDL) Locals->DumpMdl;
  3576. if (DmaIoPtr->Busy.Size != 0) {
  3577. // There was pending IO -- resume execution from the point we stopped
  3578. #define X(type,name) name = Locals->name;
  3579. HIBER_WRITE_PAGES_LOCALS_LIST (X)
  3580. #undef X
  3581. goto ResumeIO;
  3582. }
  3583. // Mark current region as busy
  3584. ASSERT (PageVa == DmaIoPtr->Used.Ptr);
  3585. PopIORegionMove (&DmaIoPtr->Busy, &DmaIoPtr->Used, (ULONG)NoPages << PAGE_SHIFT);
  3586. } else if (HiberContext->DumpStack->Init.WritePendingRoutine != 0 &&
  3587. DmaIoPtr != NULL &&
  3588. DmaIoPtr->DumpLocalData != NULL) {
  3589. if (!DmaIoPtr->DmaInitialized) {
  3590. ULONGLONG StartCount = HIBER_GET_TICK_COUNT(NULL);
  3591. Status = HiberContext->DumpStack->Init.WritePendingRoutine (IO_DUMP_WRITE_INIT,
  3592. NULL,
  3593. NULL,
  3594. DmaIoPtr->DumpLocalData);
  3595. HiberContext->PerfInfo.InitTicks += HIBER_GET_TICK_COUNT(NULL) - StartCount;
  3596. if (Status != STATUS_SUCCESS) {
  3597. DmaIoPtr->UseDma = FALSE;
  3598. }
  3599. DmaIoPtr->DmaInitialized = TRUE;
  3600. DmaIoPtr->HiberWritePagesLocals.Status = STATUS_SUCCESS;
  3601. }
  3602. PopIOWrite (HiberContext, PageVa, (ULONG)NoPages << PAGE_SHIFT, FilePage);
  3603. return;
  3604. }
  3605. //
  3606. // Page count must be below 4GB byte length
  3607. //
  3608. if (NoPages > ((((ULONG_PTR) -1) << PAGE_SHIFT) >> PAGE_SHIFT)) {
  3609. PopInternalError (POP_HIBER);
  3610. }
  3611. //
  3612. // Loop while there's data to be written
  3613. //
  3614. CMcb = (PPOP_MCB_CONTEXT) HiberContext->CurrentMcb;
  3615. MdlPage = MmGetMdlPfnArray( Mdl );
  3616. FileBase = (ULONGLONG) FilePage << PAGE_SHIFT;
  3617. Length = NoPages << PAGE_SHIFT;
  3618. while (Length != 0) {
  3619. //
  3620. // If this IO is outside the current Mcb locate the
  3621. // proper Mcb
  3622. //
  3623. if (FileBase < CMcb->Base || FileBase >= CMcb->Base + CMcb->Mcb[0].QuadPart) {
  3624. //
  3625. // If io is before this mcb, search from the begining
  3626. //
  3627. if (FileBase < CMcb->Base) {
  3628. CMcb->Mcb = CMcb->FirstMcb;
  3629. CMcb->Base = 0;
  3630. }
  3631. //
  3632. // Find the Mcb which covers the start of the io and
  3633. // make it the current mcb
  3634. //
  3635. while (FileBase >= CMcb->Base + CMcb->Mcb[0].QuadPart) {
  3636. CMcb->Base += CMcb->Mcb[0].QuadPart;
  3637. CMcb->Mcb += 2;
  3638. }
  3639. }
  3640. //
  3641. // Determine physical IoLocation and IoLength to write.
  3642. //
  3643. McbOffset = FileBase - CMcb->Base;
  3644. IoLocation.QuadPart = CMcb->Mcb[1].QuadPart + McbOffset;
  3645. //
  3646. // If the IoLength is beyond the Mcb, limit it to the Mcb
  3647. //
  3648. if (McbOffset + Length > (ULONGLONG) CMcb->Mcb[0].QuadPart) {
  3649. IoLength = (ULONG) (CMcb->Mcb[0].QuadPart - McbOffset);
  3650. } else {
  3651. IoLength = (ULONG) Length;
  3652. }
  3653. //
  3654. // If the IoLength is more pages then the largest Mdl size
  3655. // then shrink it
  3656. //
  3657. NoPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (PageVa, IoLength);
  3658. if (NoPages > IO_DUMP_MAX_MDL_PAGES) {
  3659. IoLength -= (ULONG)((NoPages - IO_DUMP_MAX_MDL_PAGES) << PAGE_SHIFT);
  3660. NoPages = IO_DUMP_MAX_MDL_PAGES;
  3661. }
  3662. //
  3663. // Debugging only
  3664. // Make sure that we may handle non-page aligned IO
  3665. // (simulate fragmented hiberfil.sys)
  3666. //
  3667. // if (IoLength > 512) IoLength = 512;
  3668. //
  3669. if (HIBER_USE_DMA (HiberContext)) {
  3670. ULONG Size;
  3671. // Do not write accross page boundaries
  3672. // to avoid memory allocation that HAL may do;
  3673. // Because of MCB's partial IOs may be smaller than one page
  3674. Size = PAGE_SIZE - (ULONG)((ULONG_PTR)PageVa & (PAGE_SIZE - 1));
  3675. if (IoLength > Size) {
  3676. IoLength = Size;
  3677. }
  3678. }
  3679. //
  3680. // Build the Mdl for the Io
  3681. //
  3682. MmInitializeMdl(Mdl, PageVa, IoLength);
  3683. Mdl->MappedSystemVa = PageVa;
  3684. Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
  3685. for (i=0; i < NoPages; i++) {
  3686. pa = MmGetPhysicalAddress((PVOID) (((ULONG_PTR)PageVa) + (i << PAGE_SHIFT)));
  3687. MdlPage[i] = (PFN_NUMBER) (pa.QuadPart >> PAGE_SHIFT);
  3688. }
  3689. //
  3690. // Write the data
  3691. //
  3692. StartCount = HIBER_GET_TICK_COUNT(NULL);
  3693. if (Locals != NULL && HIBER_USE_DMA (HiberContext)) {
  3694. Status = HiberContext->DumpStack->Init.WritePendingRoutine (IO_DUMP_WRITE_START,
  3695. &IoLocation,
  3696. Mdl,
  3697. DmaIoPtr->DumpLocalData);
  3698. if (Status != STATUS_PENDING && !NT_SUCCESS (Status)) {
  3699. DBGOUT (("WriteDMA returned bad status 0x%x -- will use PIO\n", Status));
  3700. DmaIoPtr->UseDma = FALSE;
  3701. goto RetryWithPIO;
  3702. }
  3703. } else {
  3704. RetryWithPIO:
  3705. Status = HiberContext->DumpStack->Init.WriteRoutine (&IoLocation, Mdl);
  3706. }
  3707. EndCount = HIBER_GET_TICK_COUNT(NULL);
  3708. HiberContext->PerfInfo.IoTicks += EndCount - StartCount;
  3709. //
  3710. // Keep track of the number of pages written, and dump device calls
  3711. // made for performance metric reasons
  3712. //
  3713. HiberContext->PerfInfo.PagesWritten += (ULONG)NoPages;
  3714. HiberContext->PerfInfo.DumpCount += 1;
  3715. //
  3716. // Io complete or will be complete
  3717. //
  3718. Length -= IoLength;
  3719. FileBase += IoLength;
  3720. PageVa = (PVOID) (((PUCHAR) PageVa) + IoLength);
  3721. // Check status
  3722. if (Locals != NULL) {
  3723. if (Status == STATUS_PENDING) {
  3724. #define X(type,name) Locals->name = name
  3725. HIBER_WRITE_PAGES_LOCALS_LIST (X)
  3726. #undef X
  3727. return;
  3728. ResumeIO:
  3729. Status = STATUS_SUCCESS;
  3730. }
  3731. }
  3732. if (!NT_SUCCESS(Status)) {
  3733. HiberContext->Status = Status;
  3734. break;
  3735. }
  3736. }
  3737. if (Locals != NULL) {
  3738. // Completed IO request -- mark region as free
  3739. ASSERT (PageVa == DmaIoPtr->Busy.Ptr + DmaIoPtr->Busy.Size);
  3740. PopIORegionMove (&DmaIoPtr->Free, &DmaIoPtr->Busy, DmaIoPtr->Busy.Size);
  3741. }
  3742. }
  3743. UCHAR
  3744. PopGetHiberFlags(
  3745. VOID
  3746. )
  3747. /*++
  3748. Routine Description:
  3749. Determines any hibernation flags which need to be written
  3750. into the hiber image and made visible to the osloader at
  3751. resume time
  3752. Arguments:
  3753. None
  3754. Return Value:
  3755. UCHAR containing hibernation flags. Currently defined flags:
  3756. PO_HIBER_APM_RECONNECT
  3757. --*/
  3758. {
  3759. UCHAR Flags=0;
  3760. HANDLE ApmActiveKey;
  3761. OBJECT_ATTRIBUTES ObjectAttributes;
  3762. UNICODE_STRING Name;
  3763. NTSTATUS Status;
  3764. PULONG ApmActive;
  3765. UCHAR ValueBuff[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
  3766. ULONG ResultLength;
  3767. PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuff;
  3768. PAGED_CODE();
  3769. #if defined(i386)
  3770. //
  3771. // Open the APM active key to determine if APM is running.
  3772. //
  3773. RtlInitUnicodeString(&Name, PopApmActiveFlag);
  3774. InitializeObjectAttributes(&ObjectAttributes,
  3775. &Name,
  3776. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  3777. NULL,
  3778. NULL);
  3779. Status = ZwOpenKey(&ApmActiveKey,
  3780. KEY_READ,
  3781. &ObjectAttributes);
  3782. if (NT_SUCCESS(Status)) {
  3783. //
  3784. // Query the Active value. A value of 1 indicates that APM is running.
  3785. //
  3786. RtlInitUnicodeString(&Name, PopApmFlag);
  3787. Status = ZwQueryValueKey(ApmActiveKey,
  3788. &Name,
  3789. KeyValuePartialInformation,
  3790. ValueInfo,
  3791. sizeof(ValueBuff),
  3792. &ResultLength);
  3793. ZwClose(ApmActiveKey);
  3794. if (NT_SUCCESS(Status) && (ValueInfo->Type == REG_DWORD)) {
  3795. ApmActive = (PULONG)&ValueInfo->Data;
  3796. if (*ApmActive == 1) {
  3797. Flags |= PO_HIBER_APM_RECONNECT;
  3798. }
  3799. }
  3800. }
  3801. #endif
  3802. return(Flags);
  3803. }
  3804. PMDL
  3805. PopSplitMdl(
  3806. IN PMDL Original,
  3807. IN ULONG SplitPages
  3808. )
  3809. /*++
  3810. Routine Description:
  3811. Splits a new MDL of length SplitPages out from the original MDL.
  3812. This is needed so that when we have an enormous MDL of spare pages
  3813. we do not have to map the whole thing, just the part we need.
  3814. Arguments:
  3815. Original - supplies the original MDL. The length of this MDL will
  3816. be decreated by SplitPages
  3817. SplitPages - supplies the length (in pages) of the new MDL.
  3818. Return Value:
  3819. pointer to newly allocated MDL
  3820. NULL if a new MDL could not be allocated
  3821. --*/
  3822. {
  3823. PMDL NewMdl;
  3824. ULONG Length;
  3825. PPFN_NUMBER SourcePages;
  3826. PPFN_NUMBER DestPages;
  3827. Length = SplitPages << PAGE_SHIFT;
  3828. NewMdl = ExAllocatePoolWithTag(NonPagedPool,
  3829. MmSizeOfMdl(NULL, Length),
  3830. POP_HMAP_TAG);
  3831. if (NewMdl == NULL) {
  3832. return(NULL);
  3833. }
  3834. MmInitializeMdl(NewMdl, NULL, Length);
  3835. DestPages = (PPFN_NUMBER)(NewMdl + 1);
  3836. SourcePages = (PPFN_NUMBER)(Original + 1) + BYTES_TO_PAGES(Original->ByteCount) - SplitPages;
  3837. RtlCopyMemory(DestPages, SourcePages, SplitPages * sizeof(PFN_NUMBER));
  3838. Original->ByteCount -= (SplitPages << PAGE_SIZE);
  3839. return(NewMdl);
  3840. }