Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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