mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4885 lines
138 KiB
4885 lines
138 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
hiber.c
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Ken Reneris (kenr) 13-April-1997
|
|
|
|
Revision History:
|
|
|
|
Elliot Shmukler (t-ellios) 8/7/1998 Added Hiber file compression
|
|
Andrew Kadatch (akadatch)
|
|
Added Xpress file compression
|
|
Added DMA-based IO
|
|
|
|
--*/
|
|
|
|
|
|
#include "pop.h"
|
|
#include "stdio.h" // for sprintf
|
|
#include "inbv.h"
|
|
#include "xpress.h" // XPRESS declarations
|
|
|
|
// size of buffer to store compressed data
|
|
#define POP_COMPRESSED_PAGE_SET_SIZE (((XPRESS_MAX_SIZE + 2 * XPRESS_HEADER_SIZE + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1)
|
|
|
|
// Structure used to allocate memory for hand-crafted MDL
|
|
typedef struct _DUMP_MDL {
|
|
MDL BaseMdl;
|
|
PFN_NUMBER PfnArray[POP_MAX_MDL_SIZE + 1];
|
|
} DUMP_MDL[1];
|
|
|
|
typedef struct _COMPRESSION_BLOCK {
|
|
UCHAR Buffer[XPRESS_MAX_SIZE], *Ptr;
|
|
} COMPRESSION_BLOCK, *PCOMPRESSION_BLOCK;
|
|
|
|
|
|
// Data structures for DMA-based IO
|
|
typedef struct
|
|
{
|
|
PUCHAR Beg; // ptr to the beginning of entire
|
|
PUCHAR End; // ptr to the end of memory block
|
|
|
|
PUCHAR Ptr; // ptr to beginning of region
|
|
LONG Size; // size of region after ptr
|
|
LONG SizeOvl; // size of overlapping piece starting from beginning of buffer
|
|
} IOREGION;
|
|
|
|
|
|
#define IOREGION_BUFF_PAGES 64 /* 256 KB */
|
|
#define IOREGION_BUFF_SIZE (IOREGION_BUFF_PAGES << PAGE_SHIFT)
|
|
|
|
typedef struct {
|
|
PLARGE_INTEGER FirstMcb;
|
|
PLARGE_INTEGER Mcb;
|
|
ULONGLONG Base;
|
|
} POP_MCB_CONTEXT, *PPOP_MCB_CONTEXT;
|
|
|
|
#define HIBER_WRITE_PAGES_LOCALS_LIST(X)\
|
|
X (ULONGLONG, FileBase); \
|
|
X (ULONGLONG, PhysBase); \
|
|
X (ULONG_PTR, Length); \
|
|
X (ULONGLONG, McbOffset); \
|
|
X (LARGE_INTEGER, IoLocation); \
|
|
X (PHYSICAL_ADDRESS, pa); \
|
|
X (PPOP_MCB_CONTEXT, CMcb); \
|
|
X (PVOID, PageVa); \
|
|
X (PMDL, Mdl); \
|
|
X (PPFN_NUMBER, MdlPage); \
|
|
X (PFN_NUMBER, NoPages); \
|
|
X (PFN_NUMBER, FilePage); \
|
|
X (ULONG, IoLength); \
|
|
X (ULONG, i); \
|
|
X (NTSTATUS, Status);
|
|
|
|
typedef struct
|
|
{
|
|
DUMP_MDL DumpMdl;
|
|
#define X(type,name) type name
|
|
HIBER_WRITE_PAGES_LOCALS_LIST (X)
|
|
#undef X
|
|
} HIBER_WRITE_PAGES_LOCALS;
|
|
|
|
typedef struct {
|
|
IOREGION Free, Used, Busy;
|
|
PFN_NUMBER FilePage[IOREGION_BUFF_PAGES];
|
|
PVOID DumpLocalData;
|
|
ULONG UseDma;
|
|
ULONG DmaInitialized;
|
|
|
|
struct {
|
|
PUCHAR Ptr;
|
|
ULONG Bytes;
|
|
} Chk;
|
|
|
|
HIBER_WRITE_PAGES_LOCALS HiberWritePagesLocals;
|
|
} DMA_IOREGIONS;
|
|
|
|
#define DmaIoPtr ((DMA_IOREGIONS *)(HiberContext->DmaIO))
|
|
|
|
// May we use DMA IO?
|
|
#define HIBER_USE_DMA(HiberContext) \
|
|
(DmaIoPtr != NULL && \
|
|
DmaIoPtr->UseDma && \
|
|
HiberContext->DumpStack->Init.WritePendingRoutine != NULL)
|
|
|
|
#define HbCopy(_hibercontext_,_dest_,_src_,_len_) { \
|
|
ULONGLONG _starttime_; \
|
|
\
|
|
(_hibercontext_)->PerfInfo.BytesCopied += (ULONG)(_len_); \
|
|
_starttime_ = HIBER_GET_TICK_COUNT(NULL); \
|
|
RtlCopyMemory((_dest_),(_src_),(_len_)); \
|
|
(_hibercontext_)->PerfInfo.CopyTicks += \
|
|
HIBER_GET_TICK_COUNT(NULL) - _starttime_; \
|
|
}
|
|
|
|
|
|
#ifdef HIBER_DEBUG
|
|
#define DBGOUT(x) DbgPrint x
|
|
#else
|
|
#define DBGOUT(x)
|
|
#endif
|
|
|
|
//
|
|
// The performance counter on x86 doesn't work very well during hibernate
|
|
// because interrupts are turned off and we don't get the rollovers. So use
|
|
// RDTSC instead.
|
|
//
|
|
#if !defined(i386)
|
|
#define HIBER_GET_TICK_COUNT(_x_) KeQueryPerformanceCounter(_x_).QuadPart
|
|
#else
|
|
__inline
|
|
LONGLONG
|
|
HIBER_GET_TICK_COUNT(
|
|
OUT PLARGE_INTEGER Frequency OPTIONAL
|
|
)
|
|
{
|
|
if (ARGUMENT_PRESENT(Frequency)) {
|
|
Frequency->QuadPart = (ULONGLONG)KeGetCurrentPrcb()->MHz * 1000000;
|
|
}
|
|
_asm _emit 0x0f
|
|
_asm _emit 0x31
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
extern LARGE_INTEGER KdTimerDifference;
|
|
extern UNICODE_STRING IoArcBootDeviceName;
|
|
extern PUCHAR IoLoaderArcBootDeviceName;
|
|
extern UNICODE_STRING IoArcHalDeviceName;
|
|
extern POBJECT_TYPE IoFileObjectType;
|
|
extern ULONG MmAvailablePages;
|
|
extern PFN_NUMBER MmHighestPhysicalPage;
|
|
extern ULONG MmHiberPages;
|
|
extern ULONG MmZeroPageFile;
|
|
|
|
KPROCESSOR_STATE PoWakeState;
|
|
|
|
//
|
|
// Define the size of the I/Os used to zero the hiber file
|
|
//
|
|
#define POP_ZERO_CHUNK_SIZE (64 * 1024)
|
|
|
|
VOID
|
|
RtlpGetStackLimits (
|
|
OUT PULONG_PTR LowLimit,
|
|
OUT PULONG_PTR HighLimit
|
|
);
|
|
|
|
NTSTATUS
|
|
PopCreateHiberFile (
|
|
IN PPOP_HIBER_FILE HiberFile,
|
|
IN PWCHAR NameString,
|
|
IN PLARGE_INTEGER FileSize,
|
|
IN BOOLEAN DebugHiberFile
|
|
);
|
|
|
|
NTSTATUS
|
|
PopCreateHiberLinkFile (
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
);
|
|
|
|
VOID
|
|
PopClearHiberFileSignature (
|
|
IN BOOLEAN GetStats
|
|
);
|
|
|
|
VOID
|
|
PopPreserveRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
VOID
|
|
PopCloneRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
VOID
|
|
PopDiscardRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
VOID
|
|
PopSetRange (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN ULONG Flags,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
ULONG
|
|
PopSimpleRangeCheck (
|
|
IN PPOP_MEMORY_RANGE Range
|
|
);
|
|
|
|
VOID
|
|
PopCreateDumpMdl (
|
|
IN PMDL Mdl,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER EndPage
|
|
);
|
|
|
|
PVOID
|
|
PopAllocatePages (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER NoPages
|
|
);
|
|
|
|
VOID
|
|
PopWriteHiberPages (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PVOID Page,
|
|
IN PFN_NUMBER NoPages,
|
|
IN PFN_NUMBER FilePage,
|
|
IN HIBER_WRITE_PAGES_LOCALS *Locals
|
|
);
|
|
|
|
NTSTATUS
|
|
PopWriteHiberImage (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PPO_MEMORY_IMAGE MemImage,
|
|
IN PPOP_HIBER_FILE HiberFile
|
|
);
|
|
|
|
VOID
|
|
PopUpdateHiberComplete (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN ULONG Percent
|
|
);
|
|
|
|
VOID
|
|
PopReturnMemoryForHibernate (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN BOOLEAN Unmap,
|
|
IN OUT PMDL *MdlList
|
|
);
|
|
|
|
VOID
|
|
PopAddPagesToCompressedPageSet(
|
|
IN BOOLEAN AllowDataBuffering,
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN OUT PULONG_PTR CompressedBufferOffset,
|
|
IN PVOID StartVa,
|
|
IN PFN_NUMBER NumPages,
|
|
IN OUT PPFN_NUMBER SetFilePage
|
|
);
|
|
|
|
VOID
|
|
PopEndCompressedPageSet(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN OUT PULONG_PTR CompressedBufferOffset,
|
|
IN OUT PPFN_NUMBER SetFilePage
|
|
);
|
|
|
|
UCHAR
|
|
PopGetHiberFlags(
|
|
VOID
|
|
);
|
|
|
|
PMDL
|
|
PopSplitMdl(
|
|
IN PMDL Original,
|
|
IN ULONG SplitPages
|
|
);
|
|
|
|
VOID
|
|
PopZeroHiberFile(
|
|
IN HANDLE FileHandle,
|
|
IN PFILE_OBJECT FileObject
|
|
);
|
|
|
|
PVOID
|
|
PopAllocateOwnMemory(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN ULONG Bytes,
|
|
IN ULONG Tag
|
|
);
|
|
|
|
PVOID
|
|
XPRESS_CALL
|
|
PopAllocateHiberContextCallback(
|
|
PVOID context,
|
|
int CompressionWorkspaceSize
|
|
);
|
|
|
|
VOID
|
|
PopIORegionMove (
|
|
IN IOREGION *To, // ptr to region descriptor to put bytes to
|
|
IN IOREGION *From, // ptr to region descriptor to get bytes from
|
|
IN LONG Bytes // # of bytes to add to the end of region
|
|
);
|
|
|
|
BOOLEAN
|
|
PopIOResume (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN BOOLEAN Complete
|
|
);
|
|
|
|
VOID
|
|
XPRESS_CALL
|
|
PopIOCallback (
|
|
PVOID Context,
|
|
int Compressed
|
|
);
|
|
|
|
VOID
|
|
PopIOWrite (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PUCHAR Ptr,
|
|
IN LONG Bytes,
|
|
IN PFN_NUMBER FilePage
|
|
);
|
|
|
|
VOID
|
|
PopHiberPoolInit (
|
|
PPOP_HIBER_CONTEXT HiberContext,
|
|
PVOID Memory,
|
|
ULONG Size
|
|
);
|
|
|
|
BOOLEAN
|
|
PopHiberPoolCheckFree(
|
|
PVOID HiberPoolPtr,
|
|
PVOID BlockPtr
|
|
);
|
|
|
|
PVOID
|
|
PopHiberPoolAllocFree (
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag,
|
|
PVOID MemoryPtr
|
|
);
|
|
|
|
VOID
|
|
PopDumpStatistics(
|
|
IN PPO_HIBER_PERF PerfInfo
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PopEnableHiberFile)
|
|
#pragma alloc_text(PAGE, PopCreateHiberFile)
|
|
#pragma alloc_text(PAGE, PopClearHiberFileSignature)
|
|
#pragma alloc_text(PAGE, PopAllocateHiberContext)
|
|
#pragma alloc_text(PAGE, PopCreateHiberLinkFile)
|
|
#pragma alloc_text(PAGE, PopGetHiberFlags)
|
|
#pragma alloc_text(PAGE, PopZeroHiberFile)
|
|
#pragma alloc_text(PAGE, PopAllocateHiberContextCallback)
|
|
#pragma alloc_text(PAGELK, PoSetHiberRange)
|
|
#pragma alloc_text(PAGELK, PopGatherMemoryForHibernate)
|
|
#pragma alloc_text(PAGELK, PopCloneStack)
|
|
#pragma alloc_text(PAGELK, PopPreserveRange)
|
|
#pragma alloc_text(PAGELK, PopCloneRange)
|
|
#pragma alloc_text(PAGELK, PopDiscardRange)
|
|
#pragma alloc_text(PAGELK, PopAllocatePages)
|
|
#pragma alloc_text(PAGELK, PopBuildMemoryImageHeader)
|
|
#pragma alloc_text(PAGELK, PopSaveHiberContext)
|
|
#pragma alloc_text(PAGELK, PopWriteHiberImage)
|
|
#pragma alloc_text(PAGELK, PopHiberComplete)
|
|
#pragma alloc_text(PAGELK, PopSimpleRangeCheck)
|
|
#pragma alloc_text(PAGELK, PopCreateDumpMdl)
|
|
#pragma alloc_text(PAGELK, PopWriteHiberPages)
|
|
#pragma alloc_text(PAGELK, PopUpdateHiberComplete)
|
|
#pragma alloc_text(PAGELK, PopFreeHiberContext)
|
|
#pragma alloc_text(PAGELK, PopReturnMemoryForHibernate)
|
|
#pragma alloc_text(PAGELK, PopAddPagesToCompressedPageSet)
|
|
#pragma alloc_text(PAGELK, PopEndCompressedPageSet)
|
|
#pragma alloc_text(PAGELK, PopAllocateOwnMemory)
|
|
#pragma alloc_text(PAGELK, PopIORegionMove)
|
|
#pragma alloc_text(PAGELK, PopIOResume)
|
|
#pragma alloc_text(PAGELK, PopIOCallback)
|
|
#pragma alloc_text(PAGELK, PopIOWrite)
|
|
#pragma alloc_text(PAGELK, PopHiberPoolInit)
|
|
#pragma alloc_text(PAGELK, PopHiberPoolCheckFree)
|
|
#pragma alloc_text(PAGELK, PopHiberPoolAllocFree)
|
|
#pragma alloc_text(PAGELK, PopDumpStatistics)
|
|
#ifdef HIBER_DEBUG
|
|
#pragma alloc_text(PAGELK, PopHiberPoolVfy)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PopEnableHiberFile (
|
|
IN BOOLEAN Enable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function commits or decommits the storage required to hold the
|
|
hibernation image on the boot volume.
|
|
|
|
N.B. The power policy lock must be held
|
|
|
|
Arguments:
|
|
|
|
Enable - TRUE if hibernation file is to be reserved; otherwise, false
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PDUMP_STACK_CONTEXT DumpStack;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileSize;
|
|
ULONG i;
|
|
PFN_NUMBER NoPages;
|
|
|
|
//
|
|
// If this is a disable handle it
|
|
//
|
|
|
|
if (!Enable) {
|
|
if (!PopHiberFile.FileObject) {
|
|
Status = STATUS_SUCCESS;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Disable hiber file
|
|
//
|
|
if (MmZeroPageFile) {
|
|
PopZeroHiberFile(PopHiberFile.FileHandle, PopHiberFile.FileObject);
|
|
}
|
|
|
|
ObDereferenceObject (PopHiberFile.FileObject);
|
|
ZwClose (PopHiberFile.FileHandle);
|
|
ExFreePool (PopHiberFile.PagedMcb);
|
|
RtlZeroMemory (&PopHiberFile, sizeof(PopHiberFile));
|
|
|
|
if (PopHiberFileDebug.FileObject) {
|
|
|
|
if (MmZeroPageFile) {
|
|
PopZeroHiberFile(PopHiberFileDebug.FileHandle,PopHiberFileDebug.FileObject );
|
|
}
|
|
ObDereferenceObject (PopHiberFileDebug.FileObject);
|
|
ZwClose (PopHiberFileDebug.FileHandle);
|
|
RtlZeroMemory (&PopHiberFileDebug, sizeof(PopHiberFileDebug));
|
|
}
|
|
|
|
//
|
|
// Disable hiberfile allocation
|
|
//
|
|
|
|
PopCapabilities.HiberFilePresent = FALSE;
|
|
PopHeuristics.HiberFileEnabled = FALSE;
|
|
PopHeuristics.Dirty = TRUE;
|
|
|
|
//
|
|
// recompute the policies and make the proper notification
|
|
//
|
|
PopResetCurrentPolicies ();
|
|
PopSetNotificationWork (PO_NOTIFY_CAPABILITIES);
|
|
Status = STATUS_SUCCESS;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Enable hiber file
|
|
//
|
|
|
|
if (PopHiberFile.FileObject) {
|
|
Status = STATUS_SUCCESS;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// If the hal hasn't registered an S4 handler, then it's not possible
|
|
//
|
|
|
|
if (!PopCapabilities.SystemS4) {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Compute the size required for a hibernation file
|
|
//
|
|
|
|
NoPages = 0;
|
|
for (i=0; i < MmPhysicalMemoryBlock->NumberOfRuns; i++) {
|
|
NoPages += MmPhysicalMemoryBlock->Run[i].PageCount;
|
|
}
|
|
|
|
FileSize.QuadPart = (ULONGLONG) NoPages << PAGE_SHIFT;
|
|
|
|
//
|
|
// If we've never verified that the dumpstack loads do so now
|
|
// before we allocate a huge file on the boot disk
|
|
//
|
|
|
|
if (!PopHeuristics.GetDumpStackVerified) {
|
|
Status = IoGetDumpStack ((PWCHAR)PopDumpStackPrefix,
|
|
&DumpStack,
|
|
DeviceUsageTypeHibernation,
|
|
(POP_IGNORE_UNSUPPORTED_DRIVERS & PopSimulate));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
IoFreeDumpStack (DumpStack);
|
|
PopHeuristics.GetDumpStackVerified = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create the hiberfile file
|
|
//
|
|
|
|
Status = PopCreateHiberFile (&PopHiberFile, (PWCHAR)PopHiberFileName, &FileSize, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Create the debug hiberfile file
|
|
//
|
|
|
|
if (PopSimulate & POP_DEBUG_HIBER_FILE) {
|
|
PopCreateHiberFile (&PopHiberFileDebug, (PWCHAR)PopDebugHiberFileName, &FileSize, TRUE);
|
|
}
|
|
|
|
//
|
|
// Success
|
|
//
|
|
|
|
PopCapabilities.HiberFilePresent = TRUE;
|
|
if (!PopHeuristics.HiberFileEnabled) {
|
|
PopHeuristics.HiberFileEnabled = TRUE;
|
|
PopHeuristics.Dirty = TRUE;
|
|
}
|
|
|
|
PopClearHiberFileSignature (FALSE);
|
|
|
|
Done:
|
|
PopSaveHeuristics ();
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PopCreateHiberFile (
|
|
IN PPOP_HIBER_FILE HiberFile,
|
|
IN PWCHAR NameString,
|
|
IN PLARGE_INTEGER FileSize,
|
|
IN BOOLEAN DebugHiberFile
|
|
)
|
|
{
|
|
UNICODE_STRING BaseName;
|
|
UNICODE_STRING HiberFileName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
FILE_END_OF_FILE_INFORMATION Eof;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
HANDLE FileHandle = NULL;
|
|
LONGLONG McbFileSize;
|
|
PFILE_OBJECT File = NULL;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PLARGE_INTEGER mcb;
|
|
ULONG i;
|
|
PUCHAR Bitmap;
|
|
LARGE_INTEGER ByteOffset;
|
|
KEVENT Event;
|
|
PMDL Mdl;
|
|
|
|
HiberFileName.Buffer = NULL;
|
|
mcb = NULL;
|
|
|
|
RtlInitUnicodeString (&BaseName, NameString);
|
|
|
|
HiberFileName.Length = 0;
|
|
HiberFileName.MaximumLength = IoArcBootDeviceName.Length + BaseName.Length;
|
|
HiberFileName.Buffer = ExAllocatePoolWithTag (PagedPool|POOL_COLD_ALLOCATION,
|
|
HiberFileName.MaximumLength,
|
|
POP_HIBR_TAG);
|
|
|
|
if (!HiberFileName.Buffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(&HiberFileName, &IoArcBootDeviceName);
|
|
RtlAppendUnicodeStringToString(&HiberFileName, &BaseName);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&HiberFileName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = IoCreateFile(
|
|
&FileHandle,
|
|
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FileSize,
|
|
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
|
|
0L,
|
|
FILE_SUPERSEDE,
|
|
FILE_NO_INTERMEDIATE_BUFFERING | FILE_NO_COMPRESSION | FILE_DELETE_ON_CLOSE,
|
|
(PVOID) NULL,
|
|
0L,
|
|
CreateFileTypeNone,
|
|
(PVOID) NULL,
|
|
IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
PoPrint (PO_HIBERNATE, ("PopCreateHiberFile: failed to create file %x\n", Status));
|
|
goto Done;
|
|
}
|
|
|
|
Status = ObReferenceObjectByHandle (FileHandle,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
IoFileObjectType,
|
|
KernelMode,
|
|
(PVOID *)&File,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Set the size
|
|
//
|
|
|
|
Eof.EndOfFile.QuadPart = FileSize->QuadPart;
|
|
Status = ZwSetInformationFile (
|
|
FileHandle,
|
|
&IoStatus,
|
|
&Eof,
|
|
sizeof(Eof),
|
|
FileEndOfFileInformation
|
|
);
|
|
if (Status == STATUS_PENDING) {
|
|
Status = KeWaitForSingleObject(
|
|
&File->Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status)) {
|
|
PoPrint (PO_HIBERNATE, ("PopCreateHiberFile: failed to set eof %x %x\n",
|
|
Status, IoStatus.Status
|
|
));
|
|
goto Done;
|
|
}
|
|
|
|
|
|
//
|
|
// Hibernation file needs to be on the boot partition
|
|
//
|
|
|
|
DeviceObject = File->DeviceObject;
|
|
if (!(DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION)) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get the hiber file's layout
|
|
//
|
|
|
|
Status = ZwFsControlFile (
|
|
FileHandle,
|
|
(HANDLE) NULL,
|
|
(PIO_APC_ROUTINE) NULL,
|
|
(PVOID) NULL,
|
|
&IoStatus,
|
|
FSCTL_QUERY_RETRIEVAL_POINTERS,
|
|
FileSize,
|
|
sizeof (LARGE_INTEGER),
|
|
&mcb,
|
|
sizeof (PVOID)
|
|
);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
Status = KeWaitForSingleObject(
|
|
&File->Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// We have a hibernation file. Determine the number of mcbs, and perform
|
|
// a simply sanity check on them.
|
|
//
|
|
|
|
McbFileSize = 0;
|
|
for (i=0; mcb[i].QuadPart; i += 2) {
|
|
McbFileSize += mcb[i].QuadPart;
|
|
if (mcb[i+1].HighPart < 0) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
if (McbFileSize < FileSize->QuadPart) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Done;
|
|
}
|
|
|
|
HiberFile->NonPagedMcb = mcb;
|
|
HiberFile->McbSize = (i+2) * sizeof(LARGE_INTEGER);
|
|
HiberFile->PagedMcb = ExAllocatePoolWithTag (PagedPool|POOL_COLD_ALLOCATION,
|
|
HiberFile->McbSize,
|
|
POP_HIBR_TAG);
|
|
|
|
if (!HiberFile->PagedMcb) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
memcpy (HiberFile->PagedMcb, mcb, HiberFile->McbSize);
|
|
HiberFile->FileHandle = FileHandle;
|
|
HiberFile->FileObject = File;
|
|
HiberFile->FilePages = (PFN_NUMBER) (FileSize->QuadPart >> PAGE_SHIFT);
|
|
HiberFile->McbCheck = PoSimpleCheck (0, HiberFile->PagedMcb, HiberFile->McbSize);
|
|
|
|
Done:
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (FileHandle != NULL) {
|
|
ZwClose (FileHandle);
|
|
}
|
|
if (File != NULL) {
|
|
ObDereferenceObject(File);
|
|
}
|
|
}
|
|
|
|
if (HiberFileName.Buffer) {
|
|
ExFreePool (HiberFileName.Buffer);
|
|
}
|
|
|
|
if (mcb && !DebugHiberFile) {
|
|
HiberFile->NonPagedMcb = NULL;
|
|
ExFreePool (mcb);
|
|
}
|
|
|
|
|
|
//
|
|
// If no error, then hiber file being present change one way or another -
|
|
// recompute the policies and make the proper notification
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
PopResetCurrentPolicies ();
|
|
PopSetNotificationWork (PO_NOTIFY_CAPABILITIES);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PopCreateHiberLinkFile (
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a file on the loader partition which supplies
|
|
the loader with the location of the hibernation context file
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING BaseName;
|
|
UNICODE_STRING HiberFileName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
LARGE_INTEGER FileSize;
|
|
LARGE_INTEGER ByteOffset;
|
|
PPO_IMAGE_LINK LinkImage;
|
|
PUCHAR Buffer;
|
|
ULONG Length;
|
|
HANDLE FileHandle=NULL;
|
|
|
|
Buffer = NULL;
|
|
|
|
RtlInitUnicodeString (&BaseName, PopHiberFileName);
|
|
|
|
//
|
|
// Allocate working space
|
|
//
|
|
|
|
Length = IoArcHalDeviceName.Length + BaseName.Length;
|
|
if (Length < IoArcBootDeviceName.Length + sizeof(PO_IMAGE_LINK)) {
|
|
Length = IoArcBootDeviceName.Length + sizeof(PO_IMAGE_LINK);
|
|
}
|
|
|
|
Buffer = ExAllocatePoolWithTag (PagedPool, Length, POP_HIBR_TAG);
|
|
if (!Buffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
LinkImage = (PPO_IMAGE_LINK) Buffer;
|
|
HiberFileName.Buffer = (PWCHAR) Buffer;
|
|
HiberFileName.MaximumLength = (USHORT) Length;
|
|
|
|
//
|
|
// Open hiberfil.sys on loader partition
|
|
//
|
|
|
|
HiberFileName.Length = 0;
|
|
RtlAppendUnicodeStringToString(&HiberFileName, &IoArcHalDeviceName);
|
|
RtlAppendUnicodeStringToString(&HiberFileName, &BaseName);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&HiberFileName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
FileSize.QuadPart = 0;
|
|
Status = IoCreateFile (
|
|
&FileHandle,
|
|
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
&FileSize,
|
|
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
|
0,
|
|
FILE_SUPERSEDE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_COMPRESSION | FILE_DELETE_ON_CLOSE,
|
|
(PVOID) NULL,
|
|
0L,
|
|
CreateFileTypeNone,
|
|
(PVOID) NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (Status != STATUS_SHARING_VIOLATION && Status != STATUS_ACCESS_DENIED) {
|
|
PoPrint (PO_HIBERNATE, ("PopCreateHiberLinkFile: failed to create file %x\n", Status));
|
|
}
|
|
|
|
//
|
|
// Having a link file is nice, but it's not a requirement
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Write the partition name to link to
|
|
//
|
|
|
|
LinkImage->Signature = PO_IMAGE_SIGNATURE_LINK;
|
|
Length = strlen (IoLoaderArcBootDeviceName) + 1;
|
|
memcpy (LinkImage->Name, IoLoaderArcBootDeviceName, Length);
|
|
|
|
ByteOffset.QuadPart = 0;
|
|
Status = ZwWriteFile (
|
|
FileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
LinkImage,
|
|
FIELD_OFFSET (PO_IMAGE_LINK, Name) + Length,
|
|
&ByteOffset,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Link file needs to make it to the disk
|
|
//
|
|
|
|
ZwFlushBuffersFile (FileHandle, &IoStatus);
|
|
|
|
//
|
|
// Success, keep the file around
|
|
//
|
|
|
|
HiberContext->LinkFile = TRUE;
|
|
HiberContext->LinkFileHandle = FileHandle;
|
|
|
|
Done:
|
|
if (Buffer) {
|
|
ExFreePool (Buffer);
|
|
}
|
|
|
|
if ((!NT_SUCCESS(Status)) &&
|
|
(FileHandle != NULL)) {
|
|
ZwClose (FileHandle);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
PopClearHiberFileSignature (
|
|
IN BOOLEAN GetStats
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets the signature in the hibernation image to be 0,
|
|
which indicates no context is contained in the image.
|
|
|
|
N.B. The power policy lock must be held
|
|
|
|
Arguments:
|
|
|
|
GetStats - if TRUE indicates performance statistics should be read
|
|
out of the hiberfile and written into the registry
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PUCHAR Buffer;
|
|
LARGE_INTEGER ByteOffset;
|
|
KEVENT Event;
|
|
PMDL Mdl;
|
|
|
|
if (PopHiberFile.FileObject) {
|
|
Buffer = ExAllocatePoolWithTag (NonPagedPool, PAGE_SIZE, POP_HIBR_TAG);
|
|
if (Buffer == NULL) {
|
|
return;
|
|
}
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
RtlZeroMemory (Buffer, PAGE_SIZE);
|
|
ByteOffset.QuadPart = 0;
|
|
|
|
Mdl = MmCreateMdl (NULL, Buffer, PAGE_SIZE);
|
|
MmBuildMdlForNonPagedPool (Mdl);
|
|
|
|
if (GetStats) {
|
|
Status = IoPageRead(PopHiberFile.FileObject,
|
|
Mdl,
|
|
&ByteOffset,
|
|
&Event,
|
|
&IoStatus);
|
|
if (NT_SUCCESS(Status)) {
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
if (NT_SUCCESS(IoStatus.Status)) {
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE Handle;
|
|
ULONG Data;
|
|
PPO_MEMORY_IMAGE MemImage = (PPO_MEMORY_IMAGE)Buffer;
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Power");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
Status = ZwOpenKey(&Handle,
|
|
KEY_READ | KEY_WRITE,
|
|
&ObjectAttributes);
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberElapsedTime");
|
|
Data = MemImage->PerfInfo.ElapsedTime;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberIoTime");
|
|
Data = MemImage->PerfInfo.IoTime;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberCopyTime");
|
|
Data = MemImage->PerfInfo.CopyTime;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberCopyBytes");
|
|
Data = MemImage->PerfInfo.BytesCopied;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberPagesWritten");
|
|
Data = MemImage->PerfInfo.PagesWritten;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberPagesProcessed");
|
|
Data = MemImage->PerfInfo.PagesProcessed;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberDumpCount");
|
|
Data = MemImage->PerfInfo.DumpCount;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"HiberFileRuns");
|
|
Data = MemImage->PerfInfo.FileRuns;
|
|
ZwSetValueKey(Handle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&Data,
|
|
sizeof(Data));
|
|
|
|
ZwClose(Handle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlZeroMemory (Buffer, PAGE_SIZE);
|
|
KeClearEvent(&Event);
|
|
|
|
IoSynchronousPageWrite (
|
|
PopHiberFile.FileObject,
|
|
Mdl,
|
|
&ByteOffset,
|
|
&Event,
|
|
&IoStatus
|
|
);
|
|
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
ExFreePool (Mdl);
|
|
ExFreePool (Buffer);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PopZeroHiberFile(
|
|
IN HANDLE FileHandle,
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Zeroes out a hibernation file completely. This is to prevent
|
|
any leakage of data out of the hiberfile once it has been
|
|
deleted.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies the file handle to be zeroed.
|
|
|
|
FileObject - Supplies the file object to be zeroed.
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_STANDARD_INFORMATION FileInfo;
|
|
LARGE_INTEGER Offset;
|
|
ULONGLONG Remaining;
|
|
ULONG Size;
|
|
PVOID Zeroes;
|
|
NTSTATUS Status;
|
|
PMDL Mdl;
|
|
KEVENT Event;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Get the size of the file to be zeroed
|
|
//
|
|
Status = ZwQueryInformationFile(FileHandle,
|
|
&IoStatusBlock,
|
|
&FileInfo,
|
|
sizeof(FileInfo),
|
|
FileStandardInformation);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Allocate a bunch of memory to use as zeroes
|
|
//
|
|
Zeroes = ExAllocatePoolWithTag(NonPagedPool,
|
|
POP_ZERO_CHUNK_SIZE,
|
|
'rZoP');
|
|
if (Zeroes) {
|
|
RtlZeroMemory(Zeroes, POP_ZERO_CHUNK_SIZE);
|
|
Mdl = MmCreateMdl(NULL, Zeroes, POP_ZERO_CHUNK_SIZE);
|
|
if (Mdl) {
|
|
|
|
MmBuildMdlForNonPagedPool (Mdl);
|
|
Offset.QuadPart = 0;
|
|
Remaining = FileInfo.AllocationSize.QuadPart;
|
|
Size = POP_ZERO_CHUNK_SIZE;
|
|
while (Remaining) {
|
|
if (Remaining < POP_ZERO_CHUNK_SIZE) {
|
|
Size = (ULONG)Remaining;
|
|
Mdl = MmCreateMdl(Mdl, Zeroes, Size);
|
|
MmBuildMdlForNonPagedPool(Mdl);
|
|
}
|
|
|
|
KeClearEvent(&Event);
|
|
Status = IoSynchronousPageWrite(FileObject,
|
|
Mdl,
|
|
&Offset,
|
|
&Event,
|
|
&IoStatusBlock);
|
|
if (NT_SUCCESS(Status)) {
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
PoPrint (PO_HIBERNATE | PO_ERROR,
|
|
("PopZeroHiberFile: Write of size %lx at offset %I64x failed %08lx\n",
|
|
Size,
|
|
Offset.QuadPart,
|
|
Status));
|
|
}
|
|
|
|
Offset.QuadPart += Size;
|
|
Remaining -= Size;
|
|
}
|
|
|
|
ExFreePool (Mdl);
|
|
}
|
|
ExFreePool(Zeroes);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PVOID
|
|
XPRESS_CALL
|
|
PopAllocateHiberContextCallback(
|
|
PVOID context,
|
|
int CompressionWorkspaceSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by XpressEncodeCreate to allocate XpressEncodeStream.
|
|
|
|
Arguments:
|
|
|
|
context - HiberContext
|
|
CompressionWorkspaceSize - size of block to allocate
|
|
|
|
Return Value:
|
|
|
|
Pointer to allocated memory or NULL if no enough memory
|
|
|
|
--*/
|
|
{
|
|
// Allocate the memory required for the engine's workspace
|
|
return PopAllocateOwnMemory (context, CompressionWorkspaceSize, 'Xprs');
|
|
}
|
|
|
|
PVOID
|
|
PopAllocateOwnMemory(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN ULONG Bytes,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to allocate memory that will not be hibernated
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Pointer to POP_HIBER_CONTEXT structure
|
|
Bytes - size of memory block in bytes that
|
|
may be not aligned on page boundary
|
|
|
|
Return Value:
|
|
|
|
Address of memory block or NULL if failed (status will be set in this case)
|
|
|
|
--*/
|
|
{
|
|
PVOID Ptr;
|
|
ULONG Pages;
|
|
|
|
// Get # of full pages
|
|
Pages = (Bytes + (PAGE_SIZE-1)) >> PAGE_SHIFT;
|
|
|
|
// Allocate memory
|
|
Ptr = PopAllocatePages (HiberContext, Pages);
|
|
|
|
// Check for error
|
|
if (Ptr == NULL) {
|
|
HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
// Do not hibernate this memory
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_DISCARD,
|
|
Ptr,
|
|
Pages << PAGE_SHIFT,
|
|
Tag);
|
|
}
|
|
|
|
return(Ptr);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PopAllocateHiberContext (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to allocate an initial hibernation context structure.
|
|
|
|
N.B. The power policy lock must be held
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PPOP_HIBER_CONTEXT HiberContext;
|
|
ULONG i, j, k;
|
|
PLIST_ENTRY NextEntry;
|
|
PDUMP_INITIALIZATION_CONTEXT DumpInit;
|
|
PFN_NUMBER NoPages;
|
|
PFN_NUMBER Length;
|
|
PLIST_ENTRY Link;
|
|
PPOP_MEMORY_RANGE Range;
|
|
ULONG result;
|
|
PHYSICAL_ADDRESS pa;
|
|
NTSTATUS Status;
|
|
PVOID p1;
|
|
PULONG BitmapBuffer;
|
|
|
|
// Compression Related
|
|
ULONG CompressionWorkspaceSize, Unused;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
//
|
|
// Allocate space to hold the hiber context
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
HiberContext = PopAction.HiberContext;
|
|
if (!HiberContext) {
|
|
HiberContext = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof (POP_HIBER_CONTEXT),
|
|
POP_HMAP_TAG);
|
|
if (!HiberContext) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory (HiberContext, sizeof(*HiberContext));
|
|
PopAction.HiberContext = HiberContext;
|
|
|
|
InitializeListHead (&HiberContext->ClonedRanges);
|
|
KeInitializeSpinLock (&HiberContext->Lock);
|
|
}
|
|
|
|
//
|
|
// Determine what type of hiber context for this operation
|
|
// is needed
|
|
//
|
|
|
|
if (PopAction.SystemState == PowerSystemHibernate) {
|
|
|
|
//
|
|
// For a hibernate operation, the context is written
|
|
// to the hibernation file, pages need to be set aside
|
|
// for the loaders use, and any pages not needed to
|
|
// be written to the hibernation file should also be
|
|
// set aside
|
|
//
|
|
|
|
HiberContext->WriteToFile = TRUE;
|
|
HiberContext->ReserveLoaderMemory = TRUE;
|
|
HiberContext->ReserveFreeMemory = TRUE;
|
|
HiberContext->VerifyOnWake = FALSE;
|
|
|
|
} else if (PopSimulate & POP_CRC_MEMORY) {
|
|
|
|
//
|
|
// We want to checksum all of RAM during this sleep
|
|
// operation. We don't want to reserve any pages for
|
|
// anything else since the goal here is to likely look
|
|
// for somesort of corruption of failure.
|
|
//
|
|
|
|
HiberContext->WriteToFile = FALSE;
|
|
HiberContext->ReserveLoaderMemory = FALSE;
|
|
HiberContext->ReserveFreeMemory = FALSE;
|
|
HiberContext->VerifyOnWake = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// A hiber context is not needed for this sleep
|
|
//
|
|
|
|
PopFreeHiberContext (TRUE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If there's an error in the current context, then we're done
|
|
//
|
|
|
|
if (!NT_SUCCESS(HiberContext->Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// If writting to hibernation file, get a dump driver stack
|
|
//
|
|
|
|
if (HiberContext->WriteToFile) {
|
|
|
|
//
|
|
// Get a dump stack
|
|
//
|
|
|
|
if (!HiberContext->DumpStack) {
|
|
if (!PopHiberFile.FileObject) {
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
goto Done;
|
|
}
|
|
|
|
Status = IoGetDumpStack ((PWCHAR)PopDumpStackPrefix,
|
|
&HiberContext->DumpStack,
|
|
DeviceUsageTypeHibernation,
|
|
(POP_IGNORE_UNSUPPORTED_DRIVERS & PopSimulate));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
DumpInit = &HiberContext->DumpStack->Init;
|
|
|
|
//
|
|
// N.B. For further performance improvements it may be possible
|
|
// to set DumpInit->StallRoutine to a custom routine
|
|
// in order to do some processing while the dump driver
|
|
// is waiting pointlessly before performing some hardware
|
|
// related action (such as ISR calls).
|
|
//
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Create a link file for the loader to locate the hibernation file
|
|
//
|
|
|
|
Status = PopCreateHiberLinkFile (HiberContext);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Get any hibernation flags that must be visible to the osloader
|
|
//
|
|
HiberContext->HiberFlags = PopGetHiberFlags();
|
|
}
|
|
|
|
//
|
|
// Build a map of memory
|
|
//
|
|
|
|
if (HiberContext->MemoryMap.Buffer == NULL) {
|
|
PULONG BitmapBuffer;
|
|
ULONG PageCount;
|
|
|
|
//
|
|
// Initialize a bitmap describing all of physical memory.
|
|
// For now this bitmap covers from 0-MmHighestPhysicalPage.
|
|
// To support sparse memory maps more efficiently, we could break
|
|
// this up into a bitmap for each memory block run. Probably
|
|
// not a big deal, a single bitmap costs us 4K per 128MB on x86.
|
|
//
|
|
// Note that CLEAR bits in the bitmap represent what to write out.
|
|
// This is because of the way the bitmap interfaces are defined.
|
|
//
|
|
PageCount = (ULONG)((MmHighestPhysicalPage + 32) & ~31L);
|
|
|
|
PERFINFO_HIBER_ADJUST_PAGECOUNT_FOR_BBTBUFFER(&PageCount);
|
|
|
|
BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, PageCount/8, POP_HMAP_TAG);
|
|
if (BitmapBuffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
RtlInitializeBitMap(&HiberContext->MemoryMap, BitmapBuffer, PageCount);
|
|
RtlSetAllBits(&HiberContext->MemoryMap);
|
|
|
|
for (i=0; i < MmPhysicalMemoryBlock->NumberOfRuns; i++) {
|
|
PopPreserveRange(HiberContext,
|
|
MmPhysicalMemoryBlock->Run[i].BasePage,
|
|
MmPhysicalMemoryBlock->Run[i].PageCount,
|
|
POP_MEM_TAG);
|
|
}
|
|
|
|
PERFINFO_HIBER_HANDLE_BBTBUFFER_RANGE(HiberContext);
|
|
|
|
//
|
|
// Handle kernel debugger's section
|
|
//
|
|
|
|
if (!KdPitchDebugger) {
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_CLONE,
|
|
(PVOID) &KdTimerDifference,
|
|
0,
|
|
POP_DEBUGGER_TAG);
|
|
}
|
|
|
|
//
|
|
// Get Mm hibernation ranges and info
|
|
//
|
|
|
|
MmHibernateInformation (HiberContext,
|
|
&HiberContext->HiberVa,
|
|
&HiberContext->HiberPte);
|
|
|
|
//
|
|
// Get hal hibernation ranges
|
|
//
|
|
|
|
HalLocateHiberRanges (HiberContext);
|
|
|
|
//
|
|
// Get the dump drivers stack hibernation ranges
|
|
//
|
|
|
|
if (HiberContext->DumpStack) {
|
|
IoGetDumpHiberRanges (HiberContext, HiberContext->DumpStack);
|
|
}
|
|
|
|
//
|
|
// Allocate pages for cloning
|
|
//
|
|
|
|
NoPages = 0;
|
|
Link = HiberContext->ClonedRanges.Flink;
|
|
while (Link != &HiberContext->ClonedRanges) {
|
|
Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
|
|
Link = Link->Flink;
|
|
NoPages += Range->EndPage - Range->StartPage;
|
|
}
|
|
|
|
//
|
|
// Add more for ranges which are expected to appear later
|
|
//
|
|
|
|
NoPages += 40 + ((KERNEL_LARGE_STACK_SIZE >> PAGE_SHIFT) + 2) * KeNumberProcessors;
|
|
Length = NoPages << PAGE_SHIFT;
|
|
|
|
//
|
|
// Allocate pages to hold clones
|
|
//
|
|
|
|
PopGatherMemoryForHibernate (HiberContext, NoPages, &HiberContext->Spares, TRUE);
|
|
|
|
//
|
|
// Slurp one page for doing non-aligned IOs
|
|
//
|
|
|
|
HiberContext->IoPage = PopAllocatePages (HiberContext, 1);
|
|
}
|
|
|
|
if (!NT_SUCCESS(HiberContext->Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// If the context will be written to disk, then we will
|
|
// want to use compression.
|
|
//
|
|
|
|
if(HiberContext->WriteToFile) {
|
|
|
|
// Initialize XPRESS compression engine
|
|
|
|
HiberContext->CompressionWorkspace =
|
|
(PVOID) XpressEncodeCreate (XPRESS_MAX_SIZE,
|
|
(PVOID)HiberContext,
|
|
PopAllocateHiberContextCallback,
|
|
0);
|
|
|
|
if(!HiberContext->CompressionWorkspace) {
|
|
// Not enough memory -- failure
|
|
HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to use for compression
|
|
//
|
|
// N.B. This is actually the space alloted for a compressed page set fragment
|
|
// (a collection
|
|
// of compressed buffers that will be written out together in an optimal fashion).
|
|
//
|
|
// We add 2 pages to this fragment size in order to
|
|
// allow the compression of any given page
|
|
// (and thus the addition of its compressed buffers to the fragment) to overrun the
|
|
// compression buffer without causing any great havoc.
|
|
//
|
|
// See PopAddPagesToCompressedPageSet and PopEndCompressedPageSet for details.
|
|
//
|
|
|
|
HiberContext->CompressedWriteBuffer =
|
|
PopAllocateOwnMemory(HiberContext, (POP_COMPRESSED_PAGE_SET_SIZE + 2) << PAGE_SHIFT, 'Wbfr');
|
|
if(!HiberContext->CompressedWriteBuffer) {
|
|
goto Done;
|
|
}
|
|
|
|
// Allocate space for compressed data
|
|
HiberContext->CompressionBlock =
|
|
PopAllocateOwnMemory (HiberContext, sizeof (COMPRESSION_BLOCK), 'Cblk');
|
|
if(!HiberContext->CompressionBlock)
|
|
goto Done;
|
|
|
|
// Set first output pointer
|
|
((PCOMPRESSION_BLOCK) HiberContext->CompressionBlock)->Ptr =
|
|
((PCOMPRESSION_BLOCK) HiberContext->CompressionBlock)->Buffer;
|
|
|
|
// Allocate delayed IO buffer
|
|
DmaIoPtr = NULL;
|
|
|
|
{
|
|
PUCHAR Ptr;
|
|
ULONG Size = (sizeof (DmaIoPtr[0]) + IO_DUMP_WRITE_DATA_SIZE + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);
|
|
|
|
Ptr = PopAllocateOwnMemory (HiberContext, Size + IOREGION_BUFF_SIZE , 'IObk');
|
|
if (Ptr != NULL) {
|
|
// Memory layout:
|
|
// 1. DumpLocalData (temp data for WritePendingRouting preserved between Start/Resume/Finish calls)
|
|
// 2. DmaIoPtr itself
|
|
// 3. Buffers themselves
|
|
|
|
RtlZeroMemory (Ptr, Size); // Clean IO and DumpLocalData
|
|
DmaIoPtr = (DMA_IOREGIONS *) (Ptr + IO_DUMP_WRITE_DATA_SIZE);
|
|
|
|
DmaIoPtr->DumpLocalData = Ptr;
|
|
Ptr += Size;
|
|
|
|
DmaIoPtr->Free.Beg =
|
|
DmaIoPtr->Free.Ptr =
|
|
DmaIoPtr->Used.Ptr =
|
|
DmaIoPtr->Busy.Ptr =
|
|
DmaIoPtr->Used.Beg =
|
|
DmaIoPtr->Busy.Beg = Ptr;
|
|
|
|
DmaIoPtr->Free.End =
|
|
DmaIoPtr->Used.End =
|
|
DmaIoPtr->Busy.End = Ptr + IOREGION_BUFF_SIZE;
|
|
|
|
DmaIoPtr->Free.Size = IOREGION_BUFF_SIZE;
|
|
|
|
DmaIoPtr->DmaInitialized = FALSE;
|
|
DmaIoPtr->UseDma = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the context is going to be written to disk, then
|
|
// get the map of the hibernation file
|
|
//
|
|
|
|
if (HiberContext->WriteToFile && !PopHiberFile.NonPagedMcb) {
|
|
|
|
//
|
|
// Since this writes to the physical sectors of the disk
|
|
// verify the check on the MCB array before doing it
|
|
//
|
|
|
|
if (PopHiberFile.McbCheck != PoSimpleCheck (0, PopHiberFile.PagedMcb, PopHiberFile.McbSize)) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Move the MCB array to nonpaged pool
|
|
//
|
|
|
|
PopHiberFile.NonPagedMcb = ExAllocatePoolWithTag (NonPagedPool,
|
|
PopHiberFile.McbSize,
|
|
POP_HIBR_TAG);
|
|
|
|
if (!PopHiberFile.NonPagedMcb) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
memcpy (PopHiberFile.NonPagedMcb, PopHiberFile.PagedMcb, PopHiberFile.McbSize);
|
|
|
|
//
|
|
// Dump driver stack needs an 8 page memory block
|
|
//
|
|
|
|
DumpInit->MemoryBlock = PopAllocateOwnMemory (HiberContext,
|
|
IO_DUMP_MEMORY_BLOCK_PAGES << PAGE_SHIFT,
|
|
'memD');
|
|
if (!DumpInit->MemoryBlock) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Remove common buffer pages from save area
|
|
//
|
|
|
|
if (DumpInit->CommonBufferSize & (PAGE_SIZE-1)) {
|
|
PopInternalAddToDumpFile( DumpInit, sizeof(DUMP_INITIALIZATION_CONTEXT), NULL, NULL, NULL, NULL );
|
|
PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x102,
|
|
POP_HIBER,
|
|
(ULONG_PTR)DumpInit,
|
|
(ULONG_PTR)HiberContext );
|
|
}
|
|
|
|
for (i=0; i < 2; i++) {
|
|
if (DumpInit->CommonBuffer[i]) {
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_DISCARD,
|
|
DumpInit->CommonBuffer[i],
|
|
DumpInit->CommonBufferSize,
|
|
POP_COMMON_BUFFER_TAG);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// From here on, no new pages are added to the map.
|
|
//
|
|
|
|
if (HiberContext->ReserveLoaderMemory && !HiberContext->LoaderMdl) {
|
|
|
|
//
|
|
// Have Mm remove enough pages from memory to allow the
|
|
// loader space when reloading the image, and remove them
|
|
// from the hiber context memory map.
|
|
//
|
|
|
|
PopGatherMemoryForHibernate (
|
|
HiberContext,
|
|
MmHiberPages,
|
|
&HiberContext->LoaderMdl,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
Done:
|
|
if (!NT_SUCCESS(Status) && NT_SUCCESS(HiberContext->Status)) {
|
|
HiberContext->Status = Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(HiberContext->Status)) {
|
|
PopFreeHiberContext (FALSE);
|
|
}
|
|
return HiberContext->Status;
|
|
}
|
|
|
|
VOID
|
|
PopFreeHiberContext (
|
|
IN BOOLEAN FreeAll
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases all resources allocated in the hiber context
|
|
|
|
N.B. The power policy lock must be held
|
|
|
|
Arguments:
|
|
|
|
ContextBlock - If TRUE, the hiber context structure is
|
|
freed as well
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PPOP_HIBER_CONTEXT HiberContext;
|
|
PPOP_MEMORY_RANGE Range;
|
|
PLIST_ENTRY Link;
|
|
PMDL Mdl;
|
|
|
|
HiberContext = PopAction.HiberContext;
|
|
if (!HiberContext) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Return pages gathered from mm
|
|
//
|
|
|
|
PopReturnMemoryForHibernate (HiberContext, FALSE, &HiberContext->LoaderMdl);
|
|
PopReturnMemoryForHibernate (HiberContext, TRUE, &HiberContext->Clones);
|
|
PopReturnMemoryForHibernate (HiberContext, FALSE, &HiberContext->Spares);
|
|
|
|
//
|
|
// Free the cloned range list elements
|
|
//
|
|
|
|
while (!IsListEmpty(&HiberContext->ClonedRanges)) {
|
|
Range = CONTAINING_RECORD (HiberContext->ClonedRanges.Flink, POP_MEMORY_RANGE, Link);
|
|
RemoveEntryList (&Range->Link);
|
|
ExFreePool (Range);
|
|
}
|
|
|
|
if (HiberContext->MemoryMap.Buffer) {
|
|
ExFreePool(HiberContext->MemoryMap.Buffer);
|
|
HiberContext->MemoryMap.Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Free hiber file Mcb info
|
|
//
|
|
|
|
if (PopHiberFile.NonPagedMcb) {
|
|
ExFreePool (PopHiberFile.NonPagedMcb);
|
|
PopHiberFile.NonPagedMcb = NULL;
|
|
}
|
|
|
|
//
|
|
// If this is a total free, free the header
|
|
//
|
|
|
|
if (FreeAll) {
|
|
//
|
|
// Free resources used by dump driver
|
|
//
|
|
|
|
if (HiberContext->DumpStack) {
|
|
IoFreeDumpStack (HiberContext->DumpStack);
|
|
}
|
|
|
|
//
|
|
// If there's a link file, remove it
|
|
//
|
|
|
|
if (HiberContext->LinkFile) {
|
|
ZwClose(HiberContext->LinkFileHandle);
|
|
}
|
|
|
|
//
|
|
// Sanity check all gathered pages have been returned to Mm
|
|
//
|
|
|
|
if (HiberContext->PagesOut) {
|
|
PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x103,
|
|
POP_HIBER,
|
|
(ULONG_PTR)HiberContext,
|
|
0 );
|
|
}
|
|
|
|
//
|
|
// If this is a wake, clear the signature in the image
|
|
//
|
|
|
|
if (HiberContext->Status == STATUS_WAKE_SYSTEM) {
|
|
if (PopSimulate & POP_ENABLE_HIBER_PERF) {
|
|
PopClearHiberFileSignature(TRUE);
|
|
} else {
|
|
PopClearHiberFileSignature(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free hiber context structure itself
|
|
//
|
|
|
|
PopAction.HiberContext = NULL;
|
|
ExFreePool (HiberContext);
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
PopGatherMemoryForHibernate (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER NoPages,
|
|
IN PMDL *MdlList,
|
|
IN BOOLEAN Wait
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gathers NoPages from the system for hibernation work. The
|
|
gathered pages are put onto the supplied list.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The hiber context structure
|
|
|
|
NoPages - Number of pages to gather
|
|
|
|
MdlList - Head of Mdl list to enqueue the allocated pages
|
|
|
|
Wait - TRUE if caller can wait for the pages.
|
|
|
|
Return Value:
|
|
|
|
On failure FALSE and if Wait was set the HiberContext error is
|
|
set; otheriwse, TRUE
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG Result;
|
|
PPFN_NUMBER PhysPage;
|
|
ULONG i;
|
|
ULONG_PTR Length;
|
|
PMDL Mdl;
|
|
ULONG PageCount;
|
|
|
|
Result = 0;
|
|
Length = NoPages << PAGE_SHIFT;
|
|
Mdl = ExAllocatePoolWithTag (NonPagedPool,
|
|
MmSizeOfMdl (NULL, Length),
|
|
POP_HMAP_TAG);
|
|
|
|
if (Mdl) {
|
|
//
|
|
// Call Mm to gather some pages, and keep track of how many
|
|
// we have out
|
|
//
|
|
|
|
MmInitializeMdl(Mdl, NULL, Length);
|
|
Result = MmGatherMemoryForHibernate (Mdl, Wait);
|
|
}
|
|
|
|
if (Result) {
|
|
|
|
HiberContext->PagesOut += NoPages;
|
|
PhysPage = MmGetMdlPfnArray( Mdl );
|
|
for (i=0; i < NoPages; i += PageCount) {
|
|
|
|
//
|
|
// Combine contiguous pages into a single call
|
|
// to PopDiscardRange.
|
|
//
|
|
for (PageCount = 1; (i+PageCount) < NoPages; PageCount++) {
|
|
if (PhysPage[i+PageCount-1]+1 != PhysPage[i+PageCount]) {
|
|
break;
|
|
}
|
|
}
|
|
PopDiscardRange(HiberContext, PhysPage[i], PageCount, 'htaG');
|
|
}
|
|
|
|
Mdl->Next = *MdlList;
|
|
*MdlList = Mdl;
|
|
|
|
} else {
|
|
|
|
if (Mdl) {
|
|
ExFreePool (Mdl);
|
|
}
|
|
|
|
if (Wait && NT_SUCCESS(HiberContext->Status)) {
|
|
HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
VOID
|
|
PopReturnMemoryForHibernate (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN BOOLEAN Unmap,
|
|
IN OUT PMDL *MdlList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns pages allocated from PopGatherMemoryForHibernate to
|
|
the system.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The hiber context structure
|
|
|
|
MdlList - Head of Mdl list of pages to free
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PMDL Mdl;
|
|
|
|
while (*MdlList) {
|
|
Mdl = *MdlList;
|
|
*MdlList = Mdl->Next;
|
|
|
|
HiberContext->PagesOut -= Mdl->ByteCount >> PAGE_SHIFT;
|
|
if (Unmap) {
|
|
MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
|
|
}
|
|
|
|
MmReturnMemoryForHibernate (Mdl);
|
|
ExFreePool (Mdl);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PoSetHiberRange (
|
|
IN PVOID Map,
|
|
IN ULONG Flags,
|
|
IN PVOID StartVa,
|
|
IN ULONG_PTR Length,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the virtual range to the type supplied. If the length of
|
|
the range is zero, the entire section for the address specified
|
|
is set.
|
|
|
|
Ranges are expanded to their page boundries. (E.g., starting
|
|
addresses are rounded down, and ending addresses are rounded up)
|
|
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The map to set the range in
|
|
|
|
Type - Type field for the range
|
|
|
|
Start - The starting address for the range in question
|
|
|
|
Length - The length of the range, or 0 to include an entire section
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
On failure, faulure status is updated in the HiberContext structure.
|
|
|
|
--*/
|
|
{
|
|
ULONG_PTR Start;
|
|
PFN_NUMBER StartPage;
|
|
PFN_NUMBER EndPage;
|
|
PFN_NUMBER FirstPage, PhysPage;
|
|
PFN_NUMBER RunLen;
|
|
ULONG NewFlags;
|
|
PHYSICAL_ADDRESS PhysAddr;
|
|
NTSTATUS Status;
|
|
PPOP_HIBER_CONTEXT HiberContext;
|
|
ULONG SectionLength;
|
|
|
|
|
|
HiberContext = Map;
|
|
|
|
//
|
|
// If no length, include the entire section which the datum resides in
|
|
//
|
|
|
|
if (Length == 0) {
|
|
Status = MmGetSectionRange (StartVa, &StartVa, &SectionLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
PoPrint (PO_HIBERNATE, ("PoSetHiberRange: Section for %08x not found - skipped\n", StartVa));
|
|
PopInternalError (POP_HIBER);
|
|
}
|
|
Length = SectionLength;
|
|
}
|
|
|
|
//
|
|
// Turn PO_MEM_CL_OR_NCHK into just PO_MEM_CLONE
|
|
//
|
|
if (Flags & PO_MEM_CL_OR_NCHK) {
|
|
Flags &= ~PO_MEM_CL_OR_NCHK;
|
|
Flags |= PO_MEM_CLONE;
|
|
}
|
|
|
|
Start = (ULONG_PTR) StartVa;
|
|
if (Flags & PO_MEM_PAGE_ADDRESS) {
|
|
|
|
//
|
|
// Caller passed a physical page range
|
|
//
|
|
|
|
Flags &= ~PO_MEM_PAGE_ADDRESS;
|
|
PopSetRange (HiberContext,
|
|
Flags,
|
|
(PFN_NUMBER)Start,
|
|
(PFN_NUMBER)Length,
|
|
Tag);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Round to page boundries
|
|
//
|
|
|
|
StartPage = (PFN_NUMBER)(Start >> PAGE_SHIFT);
|
|
EndPage = (PFN_NUMBER)((Start + Length + (PAGE_SIZE-1) & ~(PAGE_SIZE-1)) >> PAGE_SHIFT);
|
|
|
|
//
|
|
// Set all pages in the range
|
|
//
|
|
|
|
while (StartPage < EndPage) {
|
|
PhysAddr = MmGetPhysicalAddress((PVOID) (StartPage << PAGE_SHIFT));
|
|
FirstPage = (PFN_NUMBER) (PhysAddr.QuadPart >> PAGE_SHIFT);
|
|
|
|
//
|
|
// For how long the run is
|
|
//
|
|
|
|
for (RunLen=1; StartPage + RunLen < EndPage; RunLen += 1) {
|
|
PhysAddr = MmGetPhysicalAddress ((PVOID) ((StartPage + RunLen) << PAGE_SHIFT) );
|
|
PhysPage = (PFN_NUMBER) (PhysAddr.QuadPart >> PAGE_SHIFT);
|
|
if (FirstPage+RunLen != PhysPage) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set this run
|
|
//
|
|
|
|
PopSetRange (HiberContext, Flags, FirstPage, RunLen, Tag);
|
|
StartPage += RunLen;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PopCloneStack (
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the current stack in the memory map to be a cloned range
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The map to set the range in
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
On failure, faulure status is updated in the HiberContext structure.
|
|
|
|
--*/
|
|
{
|
|
PKTHREAD Thread;
|
|
KIRQL OldIrql;
|
|
ULONG_PTR LowLimit;
|
|
ULONG_PTR HighLimit;
|
|
|
|
KeAcquireSpinLock (&HiberContext->Lock, &OldIrql);
|
|
|
|
//
|
|
// Add local stack to clone or disable check list
|
|
//
|
|
RtlpGetStackLimits(&LowLimit, &HighLimit);
|
|
|
|
Thread = KeGetCurrentThread();
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_CLONE,
|
|
(PVOID)LowLimit,
|
|
HighLimit - LowLimit,
|
|
POP_STACK_TAG);
|
|
|
|
//
|
|
// Put local processors PCR & PRCB in clone list
|
|
//
|
|
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_CLONE,
|
|
(PVOID) KeGetPcr(),
|
|
sizeof (KPCR),
|
|
POP_PCR_TAG );
|
|
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_CLONE,
|
|
KeGetCurrentPrcb(),
|
|
sizeof (KPRCB),
|
|
POP_PCRB_TAG );
|
|
|
|
KeReleaseSpinLock (&HiberContext->Lock, OldIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
PopPreserveRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a physical memory range to the list of ranges to be preserved.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Supplies the hibernation context
|
|
|
|
StartPage - Supplies the beginning of the range
|
|
|
|
PageCount - Supplies the length of the range
|
|
|
|
Tag - supplies a tag to be used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// If this range is outside the area covered by our bitmap, then we
|
|
// will just clone it instead.
|
|
//
|
|
if (StartPage + PageCount > HiberContext->MemoryMap.SizeOfBitMap) {
|
|
PoPrint (PO_HIBERNATE,
|
|
("PopPreserveRange: range %08lx, length %lx is outside bitmap of size %lx\n",
|
|
StartPage,
|
|
PageCount,
|
|
HiberContext->MemoryMap.SizeOfBitMap));
|
|
PopCloneRange(HiberContext, StartPage, PageCount, Tag);
|
|
return;
|
|
}
|
|
|
|
PoPrint(PO_HIBERNATE,
|
|
("PopPreserveRange - setting page %08lx - %08lx, Tag %.4s\n",
|
|
StartPage,
|
|
StartPage + PageCount,
|
|
&Tag));
|
|
RtlClearBits(&HiberContext->MemoryMap, (ULONG)StartPage, (ULONG)PageCount);
|
|
}
|
|
|
|
|
|
VOID
|
|
PopDiscardRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes a physical memory range from the list of ranges to be preserved.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Supplies the hibernation context
|
|
|
|
StartPage - Supplies the beginning of the range
|
|
|
|
PageCount - Supplies the length of the range
|
|
|
|
Tag - supplies a tag to be used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFN_NUMBER sp;
|
|
PFN_NUMBER count;
|
|
//
|
|
// If this range is outside the area covered by our bitmap, then
|
|
// it's not going to get written anyway.
|
|
//
|
|
if (StartPage <= HiberContext->MemoryMap.SizeOfBitMap) {
|
|
sp = StartPage;
|
|
count = PageCount;
|
|
if (sp + count > HiberContext->MemoryMap.SizeOfBitMap) {
|
|
//
|
|
// trim PageCount
|
|
//
|
|
count = HiberContext->MemoryMap.SizeOfBitMap - sp;
|
|
}
|
|
|
|
PoPrint(PO_HIBERNATE,
|
|
("PopDiscardRange - removing page %08lx - %08lx, Tag %.4s\n",
|
|
StartPage,
|
|
StartPage + PageCount,
|
|
&Tag));
|
|
RtlSetBits(&HiberContext->MemoryMap, (ULONG)sp, (ULONG)count);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
PopCloneRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a physical memory range from the list of ranges to be cloned.
|
|
This means removing it from the list to be written and adding
|
|
an entry in the clone list.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Supplies the hibernation context
|
|
|
|
StartPage - Supplies the beginning of the range
|
|
|
|
PageCount - Supplies the length of the range
|
|
|
|
Tag - supplies a tag to be used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY Link;
|
|
PPOP_MEMORY_RANGE Range;
|
|
PFN_NUMBER EndPage;
|
|
|
|
PoPrint(PO_HIBERNATE,
|
|
("PopCloneRange - cloning page %08lx - %08lx, Tag %.4s\n",
|
|
StartPage,
|
|
StartPage + PageCount,
|
|
&Tag));
|
|
PopDiscardRange(HiberContext, StartPage, PageCount, Tag);
|
|
|
|
EndPage = StartPage + PageCount;
|
|
|
|
//
|
|
// Go through the range list. If we find an adjacent range, coalesce.
|
|
// Otherwise, insert a new range entry in sorted order.
|
|
//
|
|
Link = HiberContext->ClonedRanges.Flink;
|
|
while (Link != &HiberContext->ClonedRanges) {
|
|
Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
|
|
|
|
//
|
|
// Check for an overlapping or adjacent range.
|
|
//
|
|
if (((StartPage >= Range->StartPage) && (StartPage <= Range->EndPage)) ||
|
|
((EndPage >= Range->StartPage) && (EndPage <= Range->EndPage)) ||
|
|
((StartPage <= Range->StartPage) && (EndPage >= Range->EndPage))) {
|
|
|
|
PoPrint(PO_HIBERNATE,
|
|
("PopCloneRange - coalescing range %lx - %lx (%.4s) with range %lx - %lx\n",
|
|
StartPage,
|
|
EndPage,
|
|
&Tag,
|
|
Range->StartPage,
|
|
Range->EndPage));
|
|
|
|
//
|
|
// Coalesce this range.
|
|
//
|
|
if (StartPage < Range->StartPage) {
|
|
Range->StartPage = StartPage;
|
|
}
|
|
if (EndPage > Range->EndPage) {
|
|
Range->EndPage = EndPage;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (Range->StartPage >= StartPage) {
|
|
//
|
|
// We have found a range greater than the current one. Insert the new range
|
|
// in this position.
|
|
//
|
|
break;
|
|
}
|
|
|
|
Link = Link->Flink;
|
|
}
|
|
|
|
//
|
|
// An adjacent range was not found. Allocate a new entry and insert
|
|
// it in front of the Link entry.
|
|
//
|
|
Range = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof (POP_MEMORY_RANGE),
|
|
POP_HMAP_TAG);
|
|
if (!Range) {
|
|
if (NT_SUCCESS(HiberContext->Status)) {
|
|
HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
return ;
|
|
}
|
|
Range->Tag = Tag;
|
|
Range->StartPage = StartPage;
|
|
Range->EndPage = EndPage;
|
|
InsertTailList(Link, &Range->Link);
|
|
|
|
++HiberContext->ClonedRangeCount;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
PopGetRangeCount(
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Counts the number of ranges to be written out. This includes
|
|
the number of cloned ranges on the cloned range list and the
|
|
number of runs in the memory map.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Supplies the hibernation context.
|
|
|
|
Return Value:
|
|
|
|
Number of ranges to be written out.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG RunCount=0;
|
|
ULONG NextPage=0;
|
|
ULONG Length;
|
|
|
|
while (NextPage < HiberContext->MemoryMap.SizeOfBitMap) {
|
|
Length = RtlFindNextForwardRunClear(&HiberContext->MemoryMap,
|
|
NextPage,
|
|
&NextPage);
|
|
NextPage += Length;
|
|
++RunCount;
|
|
}
|
|
|
|
return(RunCount + HiberContext->ClonedRangeCount);
|
|
}
|
|
|
|
VOID
|
|
PopSetRange (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN ULONG Flags,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER PageCount,
|
|
IN ULONG Tag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the specified physical range in the memory map
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The map to set the range in
|
|
|
|
Type - Type to set the range too
|
|
|
|
StartPage - The first page of the range
|
|
|
|
PageCount - The length of the range in pages
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
On failure, faulure status is updated in the HiberContext structure.
|
|
|
|
--*/
|
|
{
|
|
PoPrint (PO_HIBERNATE,
|
|
("PopSetRange: Ty %04x Sp %08x Len %08x %.4s\n",
|
|
Flags,
|
|
StartPage,
|
|
PageCount,
|
|
&Tag));
|
|
|
|
if (HiberContext->MapFrozen) {
|
|
PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x104,
|
|
POP_HIBER,
|
|
(ULONG_PTR)HiberContext,
|
|
0 );
|
|
}
|
|
//
|
|
// Make sure flags which should have been cleared by now aren't still set.
|
|
//
|
|
ASSERT(!(Flags & (PO_MEM_PAGE_ADDRESS | PO_MEM_CL_OR_NCHK)));
|
|
|
|
if (Flags & PO_MEM_DISCARD) {
|
|
PopDiscardRange(HiberContext, StartPage, PageCount, Tag);
|
|
} else if (Flags & PO_MEM_CLONE) {
|
|
PopCloneRange(HiberContext, StartPage, PageCount, Tag);
|
|
} else if (Flags & PO_MEM_PRESERVE) {
|
|
PopPreserveRange(HiberContext, StartPage, PageCount, Tag);
|
|
} else {
|
|
ASSERT(FALSE);
|
|
PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x105,
|
|
POP_HIBER,
|
|
(ULONG_PTR)HiberContext,
|
|
0 );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PopResetRangeEnum(
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resets the range enumerator to start at the first range.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Supplies the hibernation context
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
HiberContext->NextCloneRange = HiberContext->ClonedRanges.Flink;
|
|
HiberContext->NextPreserve = 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
PopGetNextRange(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
OUT PPFN_NUMBER StartPage,
|
|
OUT PPFN_NUMBER EndPage,
|
|
OUT PVOID *CloneVa
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the next range to be written to the hibernation file
|
|
|
|
Arguments:
|
|
|
|
HiberContext - Supplies the hibernation context.
|
|
|
|
StartPage - Returns the starting physical page to be written.
|
|
|
|
EndPage - Returns the ending physical page (non-inclusive) to be written
|
|
|
|
CloneVa - If the range is to be cloned, returns the cloned virtual address
|
|
If the range is not cloned, returns NULL
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PPOP_MEMORY_RANGE Range;
|
|
ULONG Length;
|
|
ULONG StartIndex;
|
|
|
|
if (HiberContext->NextCloneRange != &HiberContext->ClonedRanges) {
|
|
//
|
|
// Return the next cloned range
|
|
//
|
|
Range = CONTAINING_RECORD(HiberContext->NextCloneRange, POP_MEMORY_RANGE, Link);
|
|
HiberContext->NextCloneRange = HiberContext->NextCloneRange->Flink;
|
|
|
|
*StartPage = Range->StartPage;
|
|
*EndPage = Range->EndPage;
|
|
*CloneVa = Range->CloneVa;
|
|
|
|
ASSERT(Range->CloneVa != NULL);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have enumerated all the clone ranges, return the next preserved range
|
|
//
|
|
Length = RtlFindNextForwardRunClear(&HiberContext->MemoryMap,
|
|
(ULONG)HiberContext->NextPreserve,
|
|
&StartIndex);
|
|
*StartPage = StartIndex;
|
|
*EndPage = *StartPage + Length;
|
|
HiberContext->NextPreserve = *EndPage;
|
|
*CloneVa = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
PVOID
|
|
PopAllocatePages (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PFN_NUMBER NoPages
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates memory pages from system with virtual mappings.
|
|
Pages are kept on a list and are automatically freed by
|
|
PopFreeHiberContext.
|
|
|
|
Arguments:
|
|
|
|
NoPages - No of pages to allocate
|
|
|
|
Flags - Flags for the returned pages in the physical memory map
|
|
|
|
|
|
Return Value:
|
|
|
|
Virtual address of the requested pages
|
|
|
|
--*/
|
|
{
|
|
PUCHAR Buffer=NULL;
|
|
PMDL Mdl;
|
|
ULONG result;
|
|
ULONG SpareCount;
|
|
|
|
for (; ;) {
|
|
//
|
|
// If page is available in mapped clone page list, get it
|
|
//
|
|
|
|
if (NoPages < HiberContext->NoClones) {
|
|
Buffer = HiberContext->NextClone;
|
|
HiberContext->NoClones -= NoPages;
|
|
HiberContext->NextClone += NoPages << PAGE_SHIFT;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Need more virtual address space
|
|
//
|
|
|
|
if (HiberContext->Spares) {
|
|
|
|
//
|
|
// Turn spares into virtually mapped pages. Try to limit the
|
|
// number of pages being mapped so we don't run out of PTEs
|
|
// on large memory machines.
|
|
//
|
|
if ((NoPages << PAGE_SHIFT) > PO_MAX_MAPPED_CLONES) {
|
|
SpareCount = (ULONG) (NoPages << PAGE_SHIFT);
|
|
} else {
|
|
SpareCount = PO_MAX_MAPPED_CLONES;
|
|
}
|
|
|
|
Mdl = HiberContext->Spares;
|
|
|
|
if (Mdl->ByteCount > SpareCount) {
|
|
|
|
//
|
|
// Split out a smaller MDL from the spare since it is larger
|
|
// than we really need.
|
|
//
|
|
|
|
Mdl = PopSplitMdl(Mdl, SpareCount >> PAGE_SHIFT);
|
|
if (Mdl == NULL) {
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Map the entire spare MDL
|
|
//
|
|
HiberContext->Spares = Mdl->Next;
|
|
}
|
|
Mdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
|
|
HiberContext->NextClone = MmMapLockedPages (Mdl, KernelMode);
|
|
if (HiberContext->NextClone == NULL) {
|
|
|
|
//
|
|
// Put the MDL back on the spare list so it gets cleaned up
|
|
// correctly by PopFreeHiberContext.
|
|
//
|
|
Mdl->Next = HiberContext->Spares;
|
|
HiberContext->Spares = Mdl;
|
|
break;
|
|
}
|
|
HiberContext->NoClones = Mdl->ByteCount >> PAGE_SHIFT;
|
|
Mdl->Next = HiberContext->Clones;
|
|
HiberContext->Clones = Mdl;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No spares, allocate more
|
|
//
|
|
|
|
result = PopGatherMemoryForHibernate (HiberContext,
|
|
NoPages*2,
|
|
&HiberContext->Spares,
|
|
TRUE);
|
|
|
|
if (!result) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there's a failure, mark it now
|
|
//
|
|
|
|
if (!Buffer && NT_SUCCESS(HiberContext->Status)) {
|
|
HiberContext->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
ULONG
|
|
PopSimpleRangeCheck (
|
|
PPOP_MEMORY_RANGE Range
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes a checksum for the supplied range
|
|
|
|
Arguments:
|
|
|
|
Range - The range to compute the checksum for
|
|
|
|
Return Value:
|
|
|
|
The checksum value
|
|
|
|
--*/
|
|
{
|
|
PHYSICAL_ADDRESS PhysAddr;
|
|
PFN_NUMBER sp, ep, PageLen;
|
|
ULONG Check;
|
|
DUMP_MDL DumpMdl;
|
|
PMDL Mdl;
|
|
|
|
sp = Range->StartPage;
|
|
ep = Range->EndPage;
|
|
Mdl = (PMDL) DumpMdl;
|
|
|
|
if (Range->CloneVa) {
|
|
return PoSimpleCheck (0, Range->CloneVa, (ep-sp) << PAGE_SHIFT);
|
|
}
|
|
|
|
Check = 0;
|
|
while (sp < ep) {
|
|
PopCreateDumpMdl (Mdl, sp, ep);
|
|
Check = PoSimpleCheck (Check, Mdl->MappedSystemVa, Mdl->ByteCount);
|
|
sp += Mdl->ByteCount >> PAGE_SHIFT;
|
|
}
|
|
|
|
return Check;
|
|
}
|
|
|
|
VOID
|
|
PopCreateDumpMdl (
|
|
IN OUT PMDL Mdl,
|
|
IN PFN_NUMBER StartPage,
|
|
IN PFN_NUMBER EndPage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds a dump MDl for the supplied starting address for
|
|
as many pages as can be mapped, or until EndPage is hit.
|
|
|
|
Arguments:
|
|
|
|
StartPage - The first page to map
|
|
|
|
EndPage - The ending page
|
|
|
|
Return Value:
|
|
|
|
Mdl
|
|
|
|
--*/
|
|
{
|
|
PFN_NUMBER Pages;
|
|
PPFN_NUMBER PhysPage;
|
|
|
|
// mapping better make sense
|
|
if (StartPage >= EndPage) {
|
|
PopInternalError (POP_HIBER);
|
|
}
|
|
|
|
Pages = EndPage - StartPage;
|
|
if (Pages > POP_MAX_MDL_SIZE) {
|
|
Pages = POP_MAX_MDL_SIZE;
|
|
}
|
|
|
|
MmInitializeMdl(Mdl, NULL, (Pages << PAGE_SHIFT));
|
|
|
|
PhysPage = MmGetMdlPfnArray( Mdl );
|
|
while (Pages) {
|
|
*PhysPage++ = StartPage++;
|
|
Pages -= 1;
|
|
}
|
|
|
|
MmMapMemoryDumpMdl (Mdl);
|
|
Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
|
|
|
|
// byte count must be a multiple of page size
|
|
if (Mdl->ByteCount & (PAGE_SIZE-1)) {
|
|
PopInternalAddToDumpFile( Mdl, sizeof(MDL), NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x106,
|
|
POP_HIBER,
|
|
(ULONG_PTR)Mdl,
|
|
0 );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PopHiberComplete (
|
|
IN NTSTATUS Status,
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
)
|
|
{
|
|
//
|
|
// If the return from the hal is STATUS_DEVICE_DOES_NOT_EXIST, then
|
|
// the hal doesn't know how to power off the machine.
|
|
//
|
|
|
|
if (Status == STATUS_DEVICE_DOES_NOT_EXIST) {
|
|
|
|
if (InbvIsBootDriverInstalled()) {
|
|
|
|
// Display system shut down screen
|
|
|
|
PUCHAR Bitmap1, Bitmap2;
|
|
|
|
Bitmap1 = InbvGetResourceAddress(3); // shutdown bitmap
|
|
Bitmap2 = InbvGetResourceAddress(5); // logo bitmap
|
|
|
|
InbvSolidColorFill(190,279,468,294,0);
|
|
if (Bitmap1 && Bitmap2) {
|
|
InbvBitBlt(Bitmap1, 215, 282);
|
|
InbvBitBlt(Bitmap2, 217, 111);
|
|
}
|
|
|
|
} else {
|
|
InbvDisplayString ("State saved, power off the system\n");
|
|
}
|
|
|
|
// If reseting, set the flag and return
|
|
if (PopSimulate & POP_RESET_ON_HIBER) {
|
|
HiberContext->Reset = TRUE;
|
|
return ;
|
|
}
|
|
|
|
// done... wait for power off
|
|
for (; ;) ;
|
|
}
|
|
|
|
//
|
|
// If the image is complete or the sleep completed without error,
|
|
// then the checksums are valid
|
|
//
|
|
|
|
if ((NT_SUCCESS(Status) ||
|
|
HiberContext->MemoryImage->Signature == PO_IMAGE_SIGNATURE) &&
|
|
HiberContext->VerifyOnWake) {
|
|
|
|
}
|
|
|
|
//
|
|
// Release the dump PTEs
|
|
//
|
|
|
|
MmReleaseDumpAddresses (POP_MAX_MDL_SIZE);
|
|
|
|
//
|
|
// Hiber no longer in process
|
|
//
|
|
|
|
PoHiberInProgress = FALSE;
|
|
HiberContext->Status = Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PopBuildMemoryImageHeader (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN SYSTEM_POWER_STATE SystemState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the memory map range list to a memory image structure
|
|
with a range list array. This is done to build the initial image
|
|
of the header to be written into the hibernation file, and to get
|
|
the header into one chunk of pool which is not in any other
|
|
listed range list
|
|
|
|
Arguments:
|
|
|
|
HiberContext -
|
|
|
|
SystemState -
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PPOP_MEMORY_RANGE Range;
|
|
PPO_MEMORY_IMAGE MemImage;
|
|
PLIST_ENTRY Link;
|
|
PFN_NUMBER Length;
|
|
PFN_NUMBER StartPage;
|
|
ULONG StartIndex;
|
|
ULONG Index;
|
|
PMDL Mdl;
|
|
PPO_MEMORY_RANGE_ARRAY Table;
|
|
ULONG TablePages;
|
|
ULONG NeededPages;
|
|
ULONG NoPages, i;
|
|
ULONG result;
|
|
|
|
//
|
|
// Allocate memory image structure
|
|
//
|
|
|
|
MemImage = PopAllocatePages (HiberContext, 1);
|
|
if (!MemImage) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
PoSetHiberRange (HiberContext,
|
|
PO_MEM_CLONE,
|
|
MemImage,
|
|
sizeof(*MemImage),
|
|
POP_MEMIMAGE_TAG);
|
|
|
|
RtlZeroMemory(MemImage, PAGE_SIZE);
|
|
HiberContext->MemoryImage = MemImage;
|
|
MemImage->PageSize = PAGE_SIZE;
|
|
MemImage->LengthSelf = sizeof(*MemImage);
|
|
MemImage->PageSelf = (PFN_NUMBER) MmGetPhysicalAddress(MemImage).QuadPart >> PAGE_SHIFT;
|
|
KeQuerySystemTime (&MemImage->SystemTime);
|
|
MemImage->InterruptTime = KeQueryInterruptTime();
|
|
MemImage->HiberVa = HiberContext->HiberVa;
|
|
MemImage->HiberPte = HiberContext->HiberPte;
|
|
MemImage->NoHiberPtes = POP_MAX_MDL_SIZE;
|
|
MemImage->FeatureFlags = KeFeatureBits;
|
|
MemImage->ImageType = KeProcessorArchitecture;
|
|
MemImage->HiberFlags = HiberContext->HiberFlags;
|
|
if (HiberContext->LoaderMdl) {
|
|
MemImage->NoFreePages = HiberContext->LoaderMdl->ByteCount >> PAGE_SHIFT;
|
|
}
|
|
|
|
//
|
|
// Allocate storage for clones
|
|
//
|
|
|
|
Link = HiberContext->ClonedRanges.Flink;
|
|
while (Link != &HiberContext->ClonedRanges) {
|
|
Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
|
|
Link = Link->Flink;
|
|
|
|
//
|
|
// Allocate space to make a copy of this clone
|
|
//
|
|
|
|
Length = Range->EndPage - Range->StartPage;
|
|
Range->CloneVa = PopAllocatePages(HiberContext, Length);
|
|
if (!Range->CloneVa) {
|
|
PoPrint (PO_HIBERNATE, ("PopBuildImage: Could not allocate clone for %08x - %08x\n",
|
|
Range->StartPage,
|
|
Range->EndPage));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to build the restoration map of the pages which need to
|
|
// be saved. These table pages can't be checksum in the normal
|
|
// way as they hold the checksums for the rest of memory, so they
|
|
// are allocated as ranges with no checksum and then checksums
|
|
// are explicitly added in each page. However, allocating these
|
|
// pages may change the memory map, so we need to loop until we've
|
|
// got enough storage for the restoration tables allocated in the
|
|
// memory map to contain the tables, etc..
|
|
//
|
|
|
|
TablePages = 0;
|
|
|
|
for (; ;) {
|
|
//
|
|
// Compute table pages needed, if we have enough allocated
|
|
// then freeze the memory map and build them
|
|
//
|
|
|
|
NoPages = (PopGetRangeCount(HiberContext) + PO_ENTRIES_PER_PAGE - 1) / PO_ENTRIES_PER_PAGE;
|
|
if (NoPages <= TablePages) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate more table pages
|
|
//
|
|
NeededPages = NoPages - TablePages;
|
|
Table = PopAllocatePages(HiberContext, NeededPages);
|
|
if (!Table) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
for (i=0; i<NeededPages; i++) {
|
|
Table[0].Link.EntryCount = 0;
|
|
Table[0].Link.NextTable = 0;
|
|
Table[0].Link.CheckSum = 1;
|
|
Table[0].Link.Next = HiberContext->TableHead;
|
|
HiberContext->TableHead = Table;
|
|
Table = (PPO_MEMORY_RANGE_ARRAY)((ULONG_PTR)Table + PAGE_SIZE);
|
|
}
|
|
TablePages += NeededPages;
|
|
}
|
|
|
|
//
|
|
// Freeze the memory map
|
|
//
|
|
|
|
HiberContext->MapFrozen = TRUE;
|
|
|
|
//
|
|
// Fill in the ranges on the table pages
|
|
//
|
|
|
|
Table = HiberContext->TableHead;
|
|
Index = 0;
|
|
|
|
//
|
|
// Add the cloned ranges first.
|
|
//
|
|
Link = HiberContext->ClonedRanges.Flink;
|
|
while (Link != &HiberContext->ClonedRanges) {
|
|
Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
|
|
Link = Link->Flink;
|
|
|
|
PoPrint (PO_HIBER_MAP, ("PopSave: Cloned Table %08x - %08x\n",
|
|
Range->StartPage,
|
|
Range->EndPage));
|
|
|
|
Index += 1;
|
|
if (Index >= PO_MAX_RANGE_ARRAY) {
|
|
//
|
|
// Table is full, next
|
|
//
|
|
|
|
Table[0].Link.EntryCount = PO_MAX_RANGE_ARRAY-1;
|
|
Table = Table[0].Link.Next;
|
|
if (!Table) {
|
|
PopInternalError (POP_HIBER);
|
|
}
|
|
Index = 1;
|
|
}
|
|
|
|
Table[Index].Range.PageNo = 0;
|
|
Table[Index].Range.StartPage = Range->StartPage;
|
|
Table[Index].Range.EndPage = Range->EndPage;
|
|
Table[Index].Range.CheckSum = 0;
|
|
}
|
|
|
|
//
|
|
// Now add the ranges to be preserved
|
|
//
|
|
Length = RtlFindFirstRunClear(&HiberContext->MemoryMap, &StartIndex);
|
|
StartPage = StartIndex;
|
|
while (StartPage < HiberContext->MemoryMap.SizeOfBitMap) {
|
|
Index += 1;
|
|
if (Index >= PO_MAX_RANGE_ARRAY) {
|
|
//
|
|
// Table is full, next
|
|
//
|
|
|
|
Table[0].Link.EntryCount = PO_MAX_RANGE_ARRAY-1;
|
|
Table = Table[0].Link.Next;
|
|
if (!Table) {
|
|
PopInternalError (POP_HIBER);
|
|
}
|
|
Index = 1;
|
|
}
|
|
|
|
Table[Index].Range.PageNo = 0;
|
|
Table[Index].Range.StartPage = StartPage;
|
|
Table[Index].Range.EndPage = StartPage + Length;
|
|
Table[Index].Range.CheckSum = 0;
|
|
|
|
//
|
|
// Handle the corner case where the last run exactly matches
|
|
// the end of the bitmap.
|
|
//
|
|
if (StartPage + Length == HiberContext->MemoryMap.SizeOfBitMap) {
|
|
break;
|
|
}
|
|
|
|
Length = RtlFindNextForwardRunClear(&HiberContext->MemoryMap,
|
|
(ULONG)(StartPage + Length),
|
|
&StartIndex);
|
|
StartPage = StartIndex;
|
|
}
|
|
|
|
Table[0].Link.EntryCount = Index;
|
|
return HiberContext->Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PopSaveHiberContext (
|
|
IN PPOP_HIBER_CONTEXT HiberContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called at HIGH_LEVEL just before the sleep operation to
|
|
make a snap shot of the system memory as defined by
|
|
the memory image array. Cloning and applying
|
|
checksum of the necessary pages occurs here.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The memory map
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
POP_MCB_CONTEXT CurrentMcb;
|
|
PPO_MEMORY_IMAGE MemImage;
|
|
PPOP_MEMORY_RANGE Range;
|
|
PPO_MEMORY_RANGE_ARRAY Table;
|
|
ULONG Index;
|
|
PFN_NUMBER sp, ep;
|
|
DUMP_MDL DumpMdl;
|
|
PMDL Mdl;
|
|
PUCHAR cp;
|
|
PLIST_ENTRY Link;
|
|
PFN_NUMBER PageNo;
|
|
PFN_NUMBER Pages;
|
|
NTSTATUS Status;
|
|
PPFN_NUMBER TablePage;
|
|
ULONG i;
|
|
ULONGLONG StartCount;
|
|
|
|
|
|
// Compression related
|
|
|
|
ULONG CompressedSize;
|
|
|
|
//
|
|
// Hal had better have interrupts disabled here
|
|
//
|
|
|
|
if (KeDisableInterrupts() != FALSE) {
|
|
PopInternalError (POP_HIBER);
|
|
}
|
|
|
|
MemImage = HiberContext->MemoryImage;
|
|
HiberContext->CurrentMcb = &CurrentMcb;
|
|
|
|
//
|
|
// Get the current state of the processor
|
|
//
|
|
RtlZeroMemory(&PoWakeState, sizeof(KPROCESSOR_STATE));
|
|
KeSaveStateForHibernate(&PoWakeState);
|
|
HiberContext->WakeState = &PoWakeState;
|
|
|
|
//
|
|
// If there's something already in the memory image signature then
|
|
// the system is now waking up.
|
|
//
|
|
|
|
if (MemImage->Signature) {
|
|
|
|
//
|
|
// If the debugger was active, reset it
|
|
//
|
|
|
|
if (KdDebuggerEnabled && !KdPitchDebugger) {
|
|
KdDebuggerEnabled = FALSE;
|
|
KdInitSystem (0, NULL);
|
|
}
|
|
|
|
//
|
|
// Loader feature to breakin to the debugger when someone
|
|
// presses the space bar while coming back from hibernate
|
|
//
|
|
|
|
if (KdDebuggerEnabled) {
|
|
|
|
if (MemImage->Signature == PO_IMAGE_SIGNATURE_BREAK)
|
|
{
|
|
DbgBreakPoint();
|
|
}
|
|
//
|
|
// Notify the debugger we are coming back from hibernate
|
|
//
|
|
|
|
}
|
|
|
|
return STATUS_WAKE_SYSTEM;
|
|
}
|
|
|
|
//
|
|
// Set a non-zero value in the signature for the next time
|
|
//
|
|
|
|
MemImage->Signature += 1;
|
|
|
|
//
|
|
// Initialize hibernation driver stack
|
|
//
|
|
// N.B. We must reset the display and do any INT10 here. Otherwise
|
|
// the realmode stack in the HAL will get used to do the callback
|
|
// later and that memory will be modified.
|
|
//
|
|
|
|
if (HiberContext->WriteToFile) {
|
|
|
|
if (InbvIsBootDriverInstalled()) {
|
|
|
|
PUCHAR Bitmap1, Bitmap2;
|
|
|
|
Bitmap1 = InbvGetResourceAddress(2); // hibernation bitmap
|
|
Bitmap2 = InbvGetResourceAddress(5); // logo bitmap
|
|
|
|
InbvEnableDisplayString(TRUE);
|
|
InbvAcquireDisplayOwnership();
|
|
InbvResetDisplay(); // required to reset display
|
|
InbvSolidColorFill(0,0,639,479,0);
|
|
|
|
if (Bitmap1 && Bitmap2) {
|
|
InbvBitBlt(Bitmap1, 190, 279);
|
|
InbvBitBlt(Bitmap2, 217, 111);
|
|
}
|
|
|
|
InbvSetProgressBarSubset(0, 100);
|
|
InbvSetProgressBarCoordinates(303,282);
|
|
} else {
|
|
InbvResetDisplay(); // required to reset display
|
|
}
|
|
|
|
StartCount = HIBER_GET_TICK_COUNT(NULL);
|
|
Status = IoInitializeDumpStack (HiberContext->DumpStack, NULL);
|
|
HiberContext->PerfInfo.InitTicks += HIBER_GET_TICK_COUNT(NULL) - StartCount;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
PoPrint (PO_HIBERNATE, ("PopSave: dump driver initialization failed %08x\n", Status));
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
PERFINFO_HIBER_PAUSE_LOGGING();
|
|
|
|
// **************************************
|
|
// FROM HERE OUT NO MEMORY CAN BE EDITED
|
|
// **************************************
|
|
PoHiberInProgress = TRUE;
|
|
|
|
//
|
|
// From here out no memory can be edited until the system wakes up, unless
|
|
// that memory has been explicitly accounted for. The list of memory which
|
|
// is allowed to be edited is:
|
|
//
|
|
// - the local stack on each processor
|
|
// - the kernel debuggers global data
|
|
// - the page containing the 16 PTEs used by MM for MmMapMemoryDumpMdl
|
|
// - the restoration table pages
|
|
// - the page containing the MemImage structure
|
|
// - the page containing IoPage
|
|
//
|
|
|
|
|
|
//
|
|
// Clone required pages
|
|
// (note the MemImage srtucture present at system wake will
|
|
// be the one cloned here)
|
|
//
|
|
|
|
Link = HiberContext->ClonedRanges.Flink;
|
|
while (Link != &HiberContext->ClonedRanges) {
|
|
Range = CONTAINING_RECORD (Link, POP_MEMORY_RANGE, Link);
|
|
Link = Link->Flink;
|
|
|
|
ASSERT(Range->CloneVa);
|
|
cp = Range->CloneVa;
|
|
sp = Range->StartPage;
|
|
ep = Range->EndPage;
|
|
Mdl = (PMDL) DumpMdl;
|
|
|
|
while (sp < ep) {
|
|
PopCreateDumpMdl (Mdl, sp, ep);
|
|
memcpy (cp, Mdl->MappedSystemVa, Mdl->ByteCount);
|
|
cp += Mdl->ByteCount;
|
|
sp += Mdl->ByteCount >> PAGE_SHIFT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Assign page numbers to ranges
|
|
//
|
|
// N.B. We do this here to basically prove that it can be done
|
|
// and to gather some statistics. With the addition of compression,
|
|
// the PageNo field of the Table entries is only applicable to the
|
|
// table pages since uncertain compression ratios do not allow us to
|
|
// predict where each memory range will be written.
|
|
//
|
|
|
|
TablePage = &MemImage->FirstTablePage;
|
|
Table = HiberContext->TableHead;
|
|
PageNo = PO_FIRST_RANGE_TABLE_PAGE;
|
|
while (Table) {
|
|
*TablePage = PageNo;
|
|
PageNo += 1;
|
|
|
|
for (Index=1; Index <= Table[0].Link.EntryCount; Index++) {
|
|
Table[Index].Range.PageNo = PageNo;
|
|
Pages = Table[Index].Range.EndPage - Table[Index].Range.StartPage;
|
|
PageNo += Pages;
|
|
MemImage->TotalPages += Pages;
|
|
}
|
|
|
|
TablePage = &Table[0].Link.NextTable;
|
|
Table = Table[0].Link.Next;
|
|
}
|
|
MemImage->LastFilePage = PageNo;
|
|
|
|
PoPrint (PO_HIBERNATE, ("PopSave: NoFree pages %08x\n", MemImage->NoFreePages));
|
|
PoPrint (PO_HIBERNATE, ("PopSave: Memory pages %08x (%dMB)\n", MemImage->TotalPages, MemImage->TotalPages/(PAGE_SIZE/16)));
|
|
PoPrint (PO_HIBERNATE, ("PopSave: File pages %08x (%dMB)\n", MemImage->LastFilePage, MemImage->LastFilePage/(PAGE_SIZE/16)));
|
|
PoPrint (PO_HIBERNATE, ("PopSave: HiberPte %08x for %x\n", MemImage->HiberVa, MemImage->NoHiberPtes));
|
|
|
|
//
|
|
// File should be large enough, but check
|
|
//
|
|
|
|
if (HiberContext->WriteToFile && PageNo > PopHiberFile.FilePages) {
|
|
PoPrint (PO_HIBERNATE, ("PopSave: File too small - need %x\n", PageNo));
|
|
return STATUS_DISK_FULL;
|
|
}
|
|
|
|
//
|
|
// Write the hiberfile image
|
|
//
|
|
|
|
Status = PopWriteHiberImage (HiberContext, MemImage, &PopHiberFile);
|
|
|
|
PERFINFO_HIBER_DUMP_PERF_BUFFER();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If debugging, do it again into a second image
|
|
//
|
|
|
|
if (PopSimulate & POP_DEBUG_HIBER_FILE) {
|
|
Status = PopWriteHiberImage (HiberContext, MemImage, &PopHiberFileDebug);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PopWriteHiberImage (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PPO_MEMORY_IMAGE MemImage,
|
|
IN PPOP_HIBER_FILE HiberFile
|
|
)
|
|
{
|
|
PPOP_MCB_CONTEXT CMcb;
|
|
PPOP_MEMORY_RANGE Range;
|
|
PPO_MEMORY_RANGE_ARRAY Table;
|
|
ULONG Index;
|
|
PFN_NUMBER sp, ep;
|
|
DUMP_MDL DumpMdl;
|
|
PMDL Mdl;
|
|
PUCHAR cp;
|
|
PFN_NUMBER PageNo;
|
|
PFN_NUMBER Pages;
|
|
PVOID IoPage;
|
|
NTSTATUS Status;
|
|
PPFN_NUMBER TablePage;
|
|
ULONG LastPercent;
|
|
ULONG i;
|
|
ULONG temp;
|
|
ULONG_PTR CompressedWriteOffset = 0;
|
|
PVOID CloneVa;
|
|
ULONG PoWakeCheck;
|
|
|
|
LONGLONG EndCount;
|
|
LARGE_INTEGER TickFrequency;
|
|
|
|
HiberContext->PerfInfo.StartCount = HIBER_GET_TICK_COUNT(&TickFrequency);
|
|
|
|
//
|
|
// Set the sector locations for the proper file
|
|
//
|
|
|
|
CMcb = (PPOP_MCB_CONTEXT) HiberContext->CurrentMcb;
|
|
CMcb->FirstMcb = HiberFile->NonPagedMcb;
|
|
CMcb->Mcb = HiberFile->NonPagedMcb;
|
|
CMcb->Base = 0;
|
|
IoPage = HiberContext->IoPage;
|
|
|
|
//
|
|
// Write the free page map page
|
|
//
|
|
|
|
RtlZeroMemory (IoPage, PAGE_SIZE);
|
|
if (HiberContext->LoaderMdl) {
|
|
//
|
|
// The hibernation file has one page to hold the free page map.
|
|
// If MmHiberPages is more pages than would fit, it's not possible
|
|
// to pass enough free pages to guarantee being able to reload the
|
|
// hibernation image, so don't hibernate.
|
|
//
|
|
|
|
if (MmHiberPages > PAGE_SIZE / sizeof (ULONG)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
MemImage->NoFreePages = HiberContext->LoaderMdl->ByteCount >> PAGE_SHIFT;
|
|
//
|
|
// Hibernate only if the number of free pages on the MDL is more than
|
|
// the required MmHiberPages.
|
|
//
|
|
|
|
if (MemImage->NoFreePages >= MmHiberPages) {
|
|
cp = (PUCHAR) MmGetMdlPfnArray( HiberContext->LoaderMdl );
|
|
memcpy (IoPage, cp, MmHiberPages * sizeof(PFN_NUMBER));
|
|
MemImage->NoFreePages = MmHiberPages;
|
|
} else {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// If there are no free pages available to pass to the loader, don't
|
|
// hibernate.
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
MemImage->FreeMapCheck = PoSimpleCheck(0, IoPage, PAGE_SIZE);
|
|
PopWriteHiberPages (HiberContext, IoPage, 1, PO_FREE_MAP_PAGE, NULL);
|
|
|
|
//
|
|
// Write the processors saved context
|
|
//
|
|
|
|
RtlZeroMemory (IoPage, PAGE_SIZE);
|
|
memcpy (IoPage, HiberContext->WakeState, sizeof(KPROCESSOR_STATE));
|
|
PoWakeCheck =
|
|
MemImage->WakeCheck = PoSimpleCheck(0, IoPage, sizeof(KPROCESSOR_STATE));
|
|
PopWriteHiberPages (HiberContext, IoPage, 1, PO_PROCESSOR_CONTEXT_PAGE, NULL);
|
|
temp = PoSimpleCheck(0, IoPage, sizeof(KPROCESSOR_STATE));
|
|
if (MemImage->WakeCheck != temp) {
|
|
DbgPrint("Checksum for context page changed from %lx to %lx\n",
|
|
MemImage->WakeCheck, temp);
|
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 3, MemImage->WakeCheck, temp, __LINE__);
|
|
}
|
|
temp = PoSimpleCheck(0, IoPage, PAGE_SIZE);
|
|
if (MemImage->WakeCheck != temp) {
|
|
DbgPrint("Checksum for partial context page %lx doesn't match full %lx\n",
|
|
MemImage->WakeCheck, temp);
|
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 4, MemImage->WakeCheck, temp, __LINE__);
|
|
}
|
|
|
|
//
|
|
// Before computing checksums, remove all breakpoints so they are not
|
|
// written in the saved image
|
|
//
|
|
|
|
if (KdDebuggerEnabled &&
|
|
!KdPitchDebugger &&
|
|
!(PopSimulate & POP_IGNORE_HIBER_SYMBOL_UNLOAD)) {
|
|
|
|
KdDeleteAllBreakpoints();
|
|
}
|
|
|
|
//
|
|
// Run each range, put its checksum in the restoration table
|
|
// and write each range to the file
|
|
//
|
|
|
|
Table = HiberContext->TableHead;
|
|
LastPercent = 100;
|
|
|
|
HiberContext->PerfInfo.PagesProcessed = 0;
|
|
|
|
TablePage = &MemImage->FirstTablePage;
|
|
PageNo = PO_FIRST_RANGE_TABLE_PAGE;
|
|
PopResetRangeEnum(HiberContext);
|
|
|
|
while (Table) {
|
|
|
|
// Keep track of where the page tables have been written
|
|
|
|
*TablePage = PageNo;
|
|
PageNo++;
|
|
|
|
for (Index=1; Index <= Table[0].Link.EntryCount; Index++) {
|
|
PopIOResume (HiberContext, FALSE);
|
|
|
|
PopGetNextRange(HiberContext, &sp, &ep, &CloneVa);
|
|
|
|
if ((Table[Index].Range.StartPage != sp) ||
|
|
(Table[Index].Range.EndPage != ep)) {
|
|
|
|
PoPrint(PO_ERROR,("PopWriteHiberImage: Table entry %p [%lx-%lx] does not match next range [%lx-%lx]\n",
|
|
Table+Index,
|
|
Table[Index].Range.StartPage,
|
|
Table[Index].Range.EndPage,
|
|
sp,
|
|
ep));
|
|
|
|
PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
|
|
PopInternalAddToDumpFile( Table, PAGE_SIZE, NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x107,
|
|
POP_HIBER,
|
|
(ULONG_PTR)HiberContext,
|
|
(ULONG_PTR)Table );
|
|
}
|
|
|
|
Table[Index].Range.PageNo = PageNo;
|
|
|
|
//
|
|
// Write the data to hiber file
|
|
//
|
|
|
|
if (CloneVa) {
|
|
|
|
//
|
|
// Use the cloned data which is already mapped
|
|
//
|
|
|
|
Pages = ep - sp;
|
|
|
|
// Compute the cloned range's Checksum
|
|
|
|
Table[Index].Range.CheckSum = 0;
|
|
|
|
// Add the pages to the compressed page set
|
|
// (effectively writing them out)
|
|
|
|
PopAddPagesToCompressedPageSet(TRUE,
|
|
HiberContext,
|
|
&CompressedWriteOffset,
|
|
CloneVa,
|
|
Pages,
|
|
&PageNo);
|
|
HiberContext->PerfInfo.PagesProcessed += (ULONG)Pages;
|
|
|
|
// Update the progress bar
|
|
|
|
i = (ULONG)((HiberContext->PerfInfo.PagesProcessed * 100) / MemImage->TotalPages);
|
|
|
|
if (i != LastPercent) {
|
|
LastPercent = i;
|
|
PopUpdateHiberComplete(HiberContext, LastPercent);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Map a chunk and write it, loop until done
|
|
//
|
|
Mdl = (PMDL) DumpMdl;
|
|
|
|
// Initialize Check Sum
|
|
|
|
Table[Index].Range.CheckSum = 0;
|
|
|
|
while (sp < ep) {
|
|
PopCreateDumpMdl (Mdl, sp, ep);
|
|
|
|
Pages = Mdl->ByteCount >> PAGE_SHIFT;
|
|
|
|
// Add pages to compressed page set
|
|
// (effectively writing them out)
|
|
|
|
PopAddPagesToCompressedPageSet(TRUE,
|
|
HiberContext,
|
|
&CompressedWriteOffset,
|
|
Mdl->MappedSystemVa,
|
|
Pages,
|
|
&PageNo);
|
|
sp += Pages;
|
|
HiberContext->PerfInfo.PagesProcessed += (ULONG)Pages;
|
|
|
|
// Update the progress bar
|
|
|
|
i = (ULONG)((HiberContext->PerfInfo.PagesProcessed * 100) / MemImage->TotalPages);
|
|
if (i != LastPercent) {
|
|
LastPercent = i;
|
|
PopUpdateHiberComplete(HiberContext, LastPercent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Terminate the compressed page set, since the next page
|
|
// (a table page) is uncompressed.
|
|
|
|
PopEndCompressedPageSet(HiberContext, &CompressedWriteOffset, &PageNo);
|
|
|
|
TablePage = &Table[0].Link.NextTable;
|
|
Table = Table[0].Link.Next;
|
|
}
|
|
|
|
|
|
//
|
|
// Now that the range checksums have been added to the
|
|
// restoration tables they are now complete. Compute their
|
|
// checksums and write them into the file
|
|
//
|
|
|
|
Table = HiberContext->TableHead;
|
|
PageNo = PO_FIRST_RANGE_TABLE_PAGE;
|
|
while (Table) {
|
|
Table[0].Link.CheckSum = 0;
|
|
PopWriteHiberPages (HiberContext, Table, 1, PageNo, NULL);
|
|
|
|
PageNo = Table[0].Link.NextTable;
|
|
Table = Table[0].Link.Next;
|
|
}
|
|
|
|
//
|
|
// File is complete write a valid header
|
|
//
|
|
|
|
if (MemImage->WakeCheck != PoWakeCheck) {
|
|
DbgPrint("MemImage->WakeCheck %lx doesn't make PoWakeCheck %lx\n",
|
|
MemImage->WakeCheck,
|
|
PoWakeCheck);
|
|
KeBugCheckEx(INTERNAL_POWER_ERROR, 5, MemImage->WakeCheck, PoWakeCheck, __LINE__);
|
|
}
|
|
|
|
//
|
|
// Fill in perf information so we can read it after hibernation
|
|
//
|
|
EndCount = HIBER_GET_TICK_COUNT(&TickFrequency);
|
|
HiberContext->PerfInfo.ElapsedTime = (ULONG)((EndCount - HiberContext->PerfInfo.StartCount)*1000 / TickFrequency.QuadPart);
|
|
HiberContext->PerfInfo.IoTime = (ULONG)(HiberContext->PerfInfo.IoTicks*1000 / TickFrequency.QuadPart);
|
|
HiberContext->PerfInfo.CopyTime = (ULONG)(HiberContext->PerfInfo.CopyTicks*1000 / TickFrequency.QuadPart);
|
|
HiberContext->PerfInfo.InitTime = (ULONG)(HiberContext->PerfInfo.InitTicks*1000 / TickFrequency.QuadPart);
|
|
HiberContext->PerfInfo.FileRuns = PopHiberFile.McbSize / sizeof(LARGE_INTEGER) - 1;
|
|
|
|
MemImage->Signature = PO_IMAGE_SIGNATURE;
|
|
MemImage->PerfInfo = HiberContext->PerfInfo;
|
|
MemImage->CheckSum = PoSimpleCheck(0, MemImage, sizeof(*MemImage));
|
|
PopWriteHiberPages (HiberContext, MemImage, 1, PO_IMAGE_HEADER_PAGE, NULL);
|
|
|
|
//
|
|
// Image completely written flush the controller
|
|
//
|
|
|
|
if (HiberContext->WriteToFile) {
|
|
while (NT_SUCCESS (HiberContext->Status) &&
|
|
(DmaIoPtr != NULL) &&
|
|
((DmaIoPtr->Busy.Size != 0) || (DmaIoPtr->Used.Size != 0))) {
|
|
PopIOResume (HiberContext, TRUE);
|
|
}
|
|
|
|
HiberContext->DumpStack->Init.FinishRoutine();
|
|
}
|
|
|
|
if (PopSimulate & POP_ENABLE_HIBER_PERF) {
|
|
PopDumpStatistics(&HiberContext->PerfInfo);
|
|
}
|
|
|
|
//
|
|
// Failed to write the hiberfile.
|
|
//
|
|
if ( !NT_SUCCESS(HiberContext->Status) || (PopSimulate & POP_FORCE_HIBERNATE_FAILURE) ) {
|
|
#if DBG
|
|
PoPrint (PO_ERROR, ("PopWriteHiberImage: Error occured writing the hiberfile. (%x)\n", HiberContext->Status));
|
|
PopInternalAddToDumpFile( HiberContext, sizeof(POP_HIBER_CONTEXT), NULL, NULL, NULL, NULL );
|
|
KeBugCheckEx( INTERNAL_POWER_ERROR,
|
|
0x108,
|
|
POP_HIBER,
|
|
(ULONG_PTR)HiberContext,
|
|
0 );
|
|
#else
|
|
return( (NT_SUCCESS(HiberContext->Status) && (PopSimulate & POP_FORCE_HIBERNATE_FAILURE)) ? STATUS_UNSUCCESSFUL : (HiberContext->Status) );
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Before sleeping, if the check memory bit is set verify the
|
|
// dump process didn't edit any memory pages
|
|
//
|
|
|
|
if (PopSimulate & POP_TEST_CRC_MEMORY) {
|
|
if (!(PopSimulate & POP_DEBUG_HIBER_FILE) ||
|
|
(HiberFile == &PopHiberFileDebug)) {
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tell the debugger we are hibernating
|
|
//
|
|
|
|
if (!(PopSimulate & POP_IGNORE_HIBER_SYMBOL_UNLOAD)) {
|
|
|
|
KD_SYMBOLS_INFO SymbolInfo = {0};
|
|
SymbolInfo.BaseOfDll = (PVOID)KD_HIBERNATE;
|
|
|
|
DebugService2(NULL, &SymbolInfo, BREAKPOINT_UNLOAD_SYMBOLS);
|
|
}
|
|
|
|
//
|
|
// If we want to perform a reset instead of a power down, return an
|
|
// error so we don't power down
|
|
//
|
|
|
|
if (PopSimulate & POP_RESET_ON_HIBER) {
|
|
return STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
//
|
|
// Success, continue with power off operation
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PopDumpStatistics(
|
|
IN PPO_HIBER_PERF PerfInfo
|
|
)
|
|
{
|
|
LONGLONG EndCount;
|
|
LARGE_INTEGER TickFrequency;
|
|
|
|
EndCount = HIBER_GET_TICK_COUNT(&TickFrequency);
|
|
PerfInfo->ElapsedTime = (ULONG)((EndCount - PerfInfo->StartCount)*1000 / TickFrequency.QuadPart);
|
|
PerfInfo->IoTime = (ULONG)(PerfInfo->IoTicks*1000 / TickFrequency.QuadPart);
|
|
PerfInfo->CopyTime = (ULONG)(PerfInfo->CopyTicks*1000 / TickFrequency.QuadPart);
|
|
PerfInfo->InitTime = (ULONG)(PerfInfo->InitTicks*1000 / TickFrequency.QuadPart);
|
|
PerfInfo->FileRuns = PopHiberFile.McbSize / sizeof(LARGE_INTEGER) - 1;
|
|
DbgPrint("HIBER: %lu Pages written in %lu Dumps (%lu runs).\n",
|
|
PerfInfo->PagesWritten,
|
|
PerfInfo->DumpCount,
|
|
PerfInfo->FileRuns);
|
|
DbgPrint("HIBER: %lu Pages processed (%d %% compression)\n",
|
|
PerfInfo->PagesProcessed,
|
|
PerfInfo->PagesWritten*100/PerfInfo->PagesProcessed);
|
|
DbgPrint("HIBER: Elapsed time %3d.%03d seconds\n",
|
|
PerfInfo->ElapsedTime / 1000,
|
|
PerfInfo->ElapsedTime % 1000);
|
|
DbgPrint("HIBER: I/O time %3d.%03d seconds (%2d%%) %d MB/sec\n",
|
|
PerfInfo->IoTime / 1000,
|
|
PerfInfo->IoTime % 1000,
|
|
PerfInfo->ElapsedTime ? PerfInfo->IoTime*100/PerfInfo->ElapsedTime : 0,
|
|
(PerfInfo->IoTime/100000) ? (PerfInfo->PagesWritten/(1024*1024/PAGE_SIZE)) / (PerfInfo->IoTime / 100000) : 0);
|
|
DbgPrint("HIBER: Init time %3d.%03d seconds (%2d%%)\n",
|
|
PerfInfo->InitTime / 1000,
|
|
PerfInfo->InitTime % 1000,
|
|
PerfInfo->ElapsedTime ? PerfInfo->InitTime*100/PerfInfo->ElapsedTime : 0);
|
|
DbgPrint("HIBER: Copy time %3d.%03d seconds (%2d%%) %d Bytes\n",
|
|
PerfInfo->CopyTime / 1000,
|
|
PerfInfo->CopyTime % 1000,
|
|
PerfInfo->ElapsedTime ? PerfInfo->CopyTime*100/PerfInfo->ElapsedTime : 0,
|
|
PerfInfo->BytesCopied );
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
PopUpdateHiberComplete (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN ULONG Percent
|
|
)
|
|
{
|
|
UCHAR Buffer[200];
|
|
|
|
if (InbvIsBootDriverInstalled()) {
|
|
|
|
InbvUpdateProgressBar(Percent + 1);
|
|
|
|
} else {
|
|
|
|
sprintf (Buffer, "PopSave: %d%%\r", Percent);
|
|
PoPrint (PO_HIBER_MAP, ("%s", Buffer));
|
|
if (HiberContext->WriteToFile) {
|
|
InbvDisplayString (Buffer);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if ((Percent > 0) &&
|
|
((Percent % 10) == 0) &&
|
|
(PopSimulate & POP_ENABLE_HIBER_PERF)) {
|
|
DbgPrint("HIBER: %d %% done\n",Percent);
|
|
PopDumpStatistics(&HiberContext->PerfInfo);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
PopEndCompressedPageSet(
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN OUT PULONG_PTR CompressedBufferOffset,
|
|
IN OUT PPFN_NUMBER SetFilePage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminates a compressed page set, flushing whatever remains in the compression
|
|
buffer to the Hiber file. A termination of a compressed page set allows uncompressed
|
|
pages to the be written out to the Hiber file.
|
|
|
|
See PopAddPagesToCompressedPageSet for more information on compressed page sets.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The Hiber Context.
|
|
|
|
CompressedBufferOffset - Similar to same parameter in PopAddPagesToCompressedPageSet.
|
|
|
|
Should be the CompressedBufferOffset value received
|
|
from the last call to PopAddPagesToCompressedPageSet.
|
|
Will be reset to 0 after this call in preparation
|
|
for the beginning of a new compressed page set.
|
|
|
|
SetFilePage - Similar to same parameter in PopAddPagsToCompressedPageSet.
|
|
|
|
Should be the SetFilePAge value received from the last
|
|
call to PopAddPagesToCompressedPageSet. Will be reset
|
|
to the next available file page after the end of this
|
|
compressed page set.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PFN_NUMBER Pages;
|
|
PCOMPRESSION_BLOCK Block = HiberContext->CompressionBlock;
|
|
|
|
// is there are any blocked data?
|
|
if (Block->Ptr != Block->Buffer) {
|
|
// yes, flush the block
|
|
PopAddPagesToCompressedPageSet (FALSE, // no buffering -- compress now
|
|
HiberContext,
|
|
CompressedBufferOffset,
|
|
Block->Buffer,
|
|
(PFN_NUMBER) ((Block->Ptr - Block->Buffer) >> PAGE_SHIFT),
|
|
SetFilePage);
|
|
|
|
// reset block to empty
|
|
Block->Ptr = Block->Buffer;
|
|
}
|
|
|
|
|
|
// Figure out how many pages remain in the compression buffer. Don't
|
|
// use BYTES_TO_PAGES because that will truncate to ULONG.
|
|
|
|
Pages = (PFN_NUMBER) ((*CompressedBufferOffset + (PAGE_SIZE-1)) >> PAGE_SHIFT);
|
|
|
|
if (Pages > 0) {
|
|
|
|
// Write the remaining pages out
|
|
|
|
PopWriteHiberPages(HiberContext,
|
|
(PVOID)HiberContext->CompressedWriteBuffer,
|
|
Pages,
|
|
*SetFilePage,
|
|
NULL);
|
|
|
|
// Reflect our usage of the hiber file
|
|
|
|
*SetFilePage = *SetFilePage + Pages;
|
|
}
|
|
|
|
*CompressedBufferOffset = 0;
|
|
}
|
|
|
|
VOID
|
|
PopAddPagesToCompressedPageSet(
|
|
IN BOOLEAN AllowDataBuffering,
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN OUT PULONG_PTR CompressedBufferOffset,
|
|
IN PVOID StartVa,
|
|
IN PFN_NUMBER NumPages,
|
|
IN OUT PPFN_NUMBER SetFilePage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the central call needed to write out memory pages
|
|
in a compressed fashion.
|
|
|
|
This routine takes a continuous range of mapped pages and adds
|
|
them to a compressed page set. A compressed page set is merely
|
|
a stream of compressed buffers written out contiguously within
|
|
the Hiber file. Such a contiguous layout maximizes the benefit
|
|
gained from compression by writing compressed output into
|
|
the smallest possible space.
|
|
|
|
In order to accomplish such a layout, this routine continually
|
|
compresses pages and adds them to the compression buffer pointed to
|
|
by the Hiber context. Once a certain point in that buffer is reached,
|
|
it is written out to the Hiber file and the buffer is reset to the
|
|
beginning. Each write-out of the compression buffer is placed
|
|
right after the end of the last compression buffer written.
|
|
|
|
Because of the buffering used in this algorithm, compressed buffers
|
|
may remain in the compression buffer even after the last needed
|
|
call to PopAddPagesToCompressedPageSet. In order to fully flush
|
|
the buffer, PopEndCompressedPageSet must be called.
|
|
|
|
Note that in order to write any uncompressed pages to the Hiber
|
|
file, the compressed page set needs to be terminated with
|
|
PopEndCompressedPageSet. After a compressed page set is terminated,
|
|
a new set can be initiated with a call to PopAddPagesToCompressedPageSet.
|
|
|
|
N.B. A chunk of a compressed page set that has been committed to the
|
|
Hiber file in one write operation is called a compressed page set fragment
|
|
in other places within this file.
|
|
|
|
Arguments:
|
|
|
|
AllowDataBuffering - If true input pages will be buffered, otherwise
|
|
- compressed and [possibly] written immediately
|
|
|
|
HiberContext - The Hiber context
|
|
|
|
CompressedBufferOffset - An offset into the Hiber context's compression buffer
|
|
where the addition of the next compressed buffer will
|
|
occurr.
|
|
|
|
This offset should be set to 0 at the beginning of
|
|
every compressed page set. After every call,
|
|
to PopAddPagesToCompressedPageSet this offset
|
|
will be modified to reflect the current usage of
|
|
the compression buffer.
|
|
|
|
StartVa - The starting virtual address of the pages to
|
|
add to the compressed page set.
|
|
|
|
NumPages - The number of pages to add to the compressed page set.
|
|
|
|
SetFilePage - A pointer to first page in the Hiber file that will receive
|
|
the next write-out of the compression buffer.
|
|
|
|
This page should be set to the first available Hiber file
|
|
page when the compressed page set is begun. The page will
|
|
be reset to reflect the current usage of the Hiber file
|
|
by the compressed page set after each call to
|
|
PopAddPagesToCompressedPageSet.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
ULONG_PTR BufferOffset = *CompressedBufferOffset;
|
|
PUCHAR Page = (PUCHAR)StartVa;
|
|
PFN_NUMBER i;
|
|
ULONG CompressedSize;
|
|
PFN_NUMBER NumberOfPagesToCompress;
|
|
ULONG MaxCompressedSize;
|
|
ULONG AlignedCompressedSize;
|
|
PUCHAR CompressedBuffer;
|
|
|
|
if (AllowDataBuffering) {
|
|
PCOMPRESSION_BLOCK Block = HiberContext->CompressionBlock;
|
|
|
|
// Yes, try to buffer output
|
|
if (Block->Ptr != Block->Buffer) {
|
|
// Find # of free pages left in block
|
|
NumberOfPagesToCompress = (PFN_NUMBER)
|
|
((Block->Buffer + sizeof (Block->Buffer) - Block->Ptr) >> PAGE_SHIFT);
|
|
|
|
// If it's exceed available truncate
|
|
if (NumberOfPagesToCompress > NumPages) {
|
|
NumberOfPagesToCompress = NumPages;
|
|
}
|
|
|
|
// Any free space left?
|
|
if (NumberOfPagesToCompress != 0) {
|
|
HbCopy(HiberContext, Block->Ptr, Page, NumberOfPagesToCompress << PAGE_SHIFT);
|
|
NumPages -= NumberOfPagesToCompress;
|
|
Page += NumberOfPagesToCompress << PAGE_SHIFT;
|
|
Block->Ptr += NumberOfPagesToCompress << PAGE_SHIFT;
|
|
}
|
|
|
|
// Is block full?
|
|
if (Block->Ptr == Block->Buffer + sizeof (Block->Buffer)) {
|
|
// Yes, flush the block
|
|
PopAddPagesToCompressedPageSet (FALSE, // no buffering
|
|
HiberContext,
|
|
CompressedBufferOffset,
|
|
Block->Buffer,
|
|
(PFN_NUMBER) ((Block->Ptr - Block->Buffer) >> PAGE_SHIFT),
|
|
SetFilePage);
|
|
|
|
// Reset block to empty
|
|
Block->Ptr = Block->Buffer;
|
|
}
|
|
}
|
|
|
|
NumberOfPagesToCompress = sizeof (Block->Buffer) >> PAGE_SHIFT;
|
|
|
|
// While too much to compress -- compress from original location
|
|
while (NumPages >= NumberOfPagesToCompress) {
|
|
// Write pages
|
|
PopAddPagesToCompressedPageSet (FALSE, // no buffering
|
|
HiberContext,
|
|
CompressedBufferOffset,
|
|
Page,
|
|
NumberOfPagesToCompress,
|
|
SetFilePage);
|
|
|
|
// adjust pointer and counter
|
|
Page += NumberOfPagesToCompress << PAGE_SHIFT;
|
|
NumPages -= NumberOfPagesToCompress;
|
|
}
|
|
|
|
// If anything left save it in block
|
|
// N.B.: either NumPages == 0 or there is enough space in Block
|
|
if (NumPages != 0) {
|
|
HbCopy (HiberContext, Block->Ptr, Page, NumPages << PAGE_SHIFT);
|
|
Block->Ptr += NumPages << PAGE_SHIFT;
|
|
}
|
|
|
|
// done
|
|
return;
|
|
}
|
|
|
|
// First make sure values of constants match our assumptions
|
|
|
|
#if XPRESS_HEADER_SIZE < XPRESS_HEADER_STRING_SIZE + 8
|
|
#error -- XPRESS_HEADER_SIZE shall be at least (XPRESS_HEADER_STRING_SIZE + 8)
|
|
#endif
|
|
|
|
#if XPRESS_MAX_SIZE < PAGE_SIZE || XPRESS_MAX_SIZE % PAGE_SIZE != 0
|
|
#error -- XPRESS_MAX_SIZE shall be multiple of PAGE_SIZE
|
|
#endif
|
|
|
|
#if (XPRESS_ALIGNMENT & (XPRESS_ALIGNMENT - 1)) != 0
|
|
#error -- XPRESS_ALIGNMENT shall be power of 2
|
|
#endif
|
|
|
|
#if XPRESS_HEADER_SIZE % XPRESS_ALIGNMENT != 0
|
|
#error -- XPRESS_HEADER_SIZE shall be multiple of XPRESS_ALIGNMENT
|
|
#endif
|
|
|
|
// make sure that compressed buffer and its header will fit into output buffer
|
|
#if XPRESS_MAX_SIZE + XPRESS_HEADER + PAGE_SIZE - 1 > (POP_COMPRESSED_PAGE_SET_SIZE << PAGE_SHIFT)
|
|
#error -- POP_COMPRESSED_PAGE_SET_SIZE is too small
|
|
#endif
|
|
|
|
// Real compression starts here
|
|
|
|
// Loop through all the pages ...
|
|
for (i = 0; i < NumPages; i += NumberOfPagesToCompress) {
|
|
|
|
NumberOfPagesToCompress = XPRESS_MAX_PAGES;
|
|
if (NumberOfPagesToCompress > NumPages - i) {
|
|
NumberOfPagesToCompress = NumPages - i;
|
|
}
|
|
|
|
// If compressed data occupies more than 87.5% = 7/8 of original store data as is
|
|
MaxCompressedSize = ((ULONG)NumberOfPagesToCompress * 7) * (PAGE_SIZE / 8);
|
|
|
|
|
|
// Is the buffer use beyond the write-out threshold?
|
|
|
|
//
|
|
// N.B. The buffer must extend sufficiently beyond the threshold
|
|
// the allow the last compression operation (that one that writes
|
|
// beyond the threshold) to always succeed.
|
|
//
|
|
|
|
if (BufferOffset + (NumberOfPagesToCompress << PAGE_SHIFT) + XPRESS_HEADER_SIZE > (POP_COMPRESSED_PAGE_SET_SIZE << PAGE_SHIFT)) {
|
|
// Write out the compression buffer bytes below the threshold
|
|
|
|
PopWriteHiberPages(HiberContext,
|
|
(PVOID)HiberContext->CompressedWriteBuffer,
|
|
BufferOffset >> PAGE_SHIFT,
|
|
*SetFilePage,
|
|
NULL);
|
|
|
|
// We have used some pages in the Hiber file with the above write,
|
|
// indicate that our next Hiber file page will be beyond those used pages.
|
|
|
|
*SetFilePage = *SetFilePage + (BufferOffset >> PAGE_SHIFT);
|
|
|
|
// Move buffer bytes that are above the write-out threshold to the
|
|
// beginning of the buffer
|
|
|
|
if (BufferOffset & (PAGE_SIZE - 1)) {
|
|
HbCopy(HiberContext,
|
|
HiberContext->CompressedWriteBuffer,
|
|
HiberContext->CompressedWriteBuffer + (BufferOffset & ~(PAGE_SIZE - 1)),
|
|
(ULONG)BufferOffset & (PAGE_SIZE - 1));
|
|
}
|
|
|
|
// Reset the buffer offset back to the beginning of the buffer but right
|
|
// after any above-threshold buffer bytes that we will move to the beginning
|
|
// of the buffer
|
|
|
|
BufferOffset &= PAGE_SIZE - 1;
|
|
}
|
|
|
|
|
|
// Remember output position
|
|
|
|
CompressedBuffer = HiberContext->CompressedWriteBuffer + BufferOffset;
|
|
|
|
// Clear the header
|
|
RtlZeroMemory (CompressedBuffer, XPRESS_HEADER_SIZE);
|
|
|
|
|
|
// Compress pages into the compression buffer
|
|
|
|
if (HIBER_USE_DMA (HiberContext)) {
|
|
// Try to resume IO calling callback each 8192 bytes
|
|
CompressedSize = XpressEncode ((XpressEncodeStream) (HiberContext->CompressionWorkspace),
|
|
CompressedBuffer + XPRESS_HEADER_SIZE,
|
|
MaxCompressedSize,
|
|
(PVOID) Page,
|
|
(ULONG)NumberOfPagesToCompress << PAGE_SHIFT,
|
|
PopIOCallback,
|
|
HiberContext,
|
|
8192);
|
|
} else {
|
|
// No need for callbacks -- compress everything at once
|
|
CompressedSize = XpressEncode ((XpressEncodeStream) (HiberContext->CompressionWorkspace),
|
|
CompressedBuffer + XPRESS_HEADER_SIZE,
|
|
MaxCompressedSize,
|
|
(PVOID) Page,
|
|
(ULONG)NumberOfPagesToCompress << PAGE_SHIFT,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
// If compression failed copy data as is original
|
|
|
|
if (CompressedSize >= MaxCompressedSize) {
|
|
CompressedSize = (ULONG)NumberOfPagesToCompress << PAGE_SHIFT;
|
|
HbCopy (HiberContext,
|
|
CompressedBuffer + XPRESS_HEADER_SIZE,
|
|
(PVOID) Page,
|
|
CompressedSize);
|
|
}
|
|
|
|
//
|
|
// Fill the header
|
|
//
|
|
|
|
|
|
// Magic bytes (LZNT1 block cannot start from 0x81,0x81)
|
|
RtlCopyMemory (CompressedBuffer, XPRESS_HEADER_STRING, XPRESS_HEADER_STRING_SIZE);
|
|
|
|
|
|
// Size of original and compressed data
|
|
{
|
|
ULONG dw = ((CompressedSize - 1) << 10) + ((ULONG)NumberOfPagesToCompress - 1);
|
|
|
|
#if XPRESS_MAX_SIZE > (1 << 22)
|
|
#error -- XPRESS_MAX_SIZE shall not exceed 4 MB
|
|
#endif
|
|
|
|
CompressedBuffer[XPRESS_HEADER_STRING_SIZE] = (UCHAR) dw;
|
|
CompressedBuffer[XPRESS_HEADER_STRING_SIZE+1] = (UCHAR) (dw >> 8);
|
|
CompressedBuffer[XPRESS_HEADER_STRING_SIZE+2] = (UCHAR) (dw >> 16);
|
|
CompressedBuffer[XPRESS_HEADER_STRING_SIZE+3] = (UCHAR) (dw >> 24);
|
|
}
|
|
|
|
// Align compressed data on 8-byte boundary
|
|
AlignedCompressedSize = (CompressedSize + (XPRESS_ALIGNMENT - 1)) & ~(XPRESS_ALIGNMENT - 1);
|
|
if (CompressedSize != AlignedCompressedSize) {
|
|
// Fill up data with zeroes until aligned
|
|
RtlZeroMemory (CompressedBuffer + XPRESS_HEADER_SIZE + CompressedSize, AlignedCompressedSize - CompressedSize);
|
|
}
|
|
|
|
// Indicate our new usage of the buffer
|
|
|
|
BufferOffset += AlignedCompressedSize + XPRESS_HEADER_SIZE;
|
|
|
|
// Move on to the virtual address of the next page
|
|
|
|
Page += NumberOfPagesToCompress << PAGE_SHIFT;
|
|
}
|
|
|
|
*CompressedBufferOffset = BufferOffset;
|
|
}
|
|
|
|
|
|
VOID
|
|
PopIORegionMove (
|
|
IN IOREGION *To, // ptr to region descriptor to put bytes to
|
|
IN IOREGION *From, // ptr to region descriptor to get bytes from
|
|
IN LONG Bytes // # of bytes to move from the beginning of one region to the end of another
|
|
)
|
|
{
|
|
ASSERT((Bytes & (PAGE_SIZE-1)) == 0);
|
|
|
|
if (To->Size != To->End - To->Ptr) {
|
|
ASSERT (To->Ptr + To->Size == From->Ptr);
|
|
To->Size += Bytes;
|
|
ASSERT (To->Size <= To->End - To->Ptr);
|
|
} else {
|
|
ASSERT (To->Beg + To->SizeOvl == From->Ptr);
|
|
To->SizeOvl += Bytes;
|
|
ASSERT (To->Size + To->SizeOvl <= To->End - To->Beg);
|
|
}
|
|
|
|
ASSERT (Bytes <= From->Size && From->Size <= From->End - From->Ptr);
|
|
From->Size -= Bytes;
|
|
From->Ptr += Bytes;
|
|
if (From->Ptr == From->End) {
|
|
ASSERT (From->Size == 0);
|
|
From->Ptr = From->Beg;
|
|
From->Size = From->SizeOvl;
|
|
From->SizeOvl = 0;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
XPRESS_CALL
|
|
PopIOCallback (
|
|
PVOID Context,
|
|
int compressed
|
|
)
|
|
{
|
|
PPOP_HIBER_CONTEXT HiberContext = Context;
|
|
|
|
if (HiberContext == NULL || DmaIoPtr == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (DmaIoPtr->Busy.Size == 0 && DmaIoPtr->Used.Size == 0)
|
|
return;
|
|
|
|
PopIOResume (Context, FALSE);
|
|
}
|
|
|
|
BOOLEAN PopIOResume (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN BOOLEAN Complete
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
// If there were error don't even bother
|
|
if (!NT_SUCCESS(HiberContext->Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if (DmaIoPtr == NULL) {
|
|
return(TRUE);
|
|
}
|
|
|
|
// if delayed operation then resume or complete it
|
|
while (DmaIoPtr->Busy.Size != 0) {
|
|
|
|
status = HiberContext->DumpStack->Init.WritePendingRoutine (Complete?IO_DUMP_WRITE_FINISH:IO_DUMP_WRITE_RESUME,
|
|
NULL,
|
|
NULL,
|
|
DmaIoPtr->DumpLocalData);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
// Pending IO; shall never happen if Complete
|
|
ASSERT (!Complete);
|
|
return(TRUE);
|
|
}
|
|
|
|
// If there were error then don't care
|
|
if (!NT_SUCCESS (status)) {
|
|
HiberContext->Status = status;
|
|
return(FALSE);
|
|
}
|
|
|
|
// Now, resume PopWriteHiberPages
|
|
PopWriteHiberPages (HiberContext,
|
|
NULL,
|
|
0,
|
|
0,
|
|
&DmaIoPtr->HiberWritePagesLocals);
|
|
if (!NT_SUCCESS (HiberContext->Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// If pending IO completed and we had to wait -- do not start new one
|
|
if (DmaIoPtr->Busy.Size == 0 && Complete) {
|
|
return(TRUE);
|
|
}
|
|
|
|
// If not completed and do no wait -- return
|
|
if (DmaIoPtr->Busy.Size != 0 && !Complete) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
while (DmaIoPtr->Used.Size >= PAGE_SIZE) {
|
|
ULONG_PTR i, j;
|
|
ULONG_PTR NoPages;
|
|
ULONG_PTR Length;
|
|
PUCHAR PageVa;
|
|
PFN_NUMBER FilePage;
|
|
|
|
// Obtain size of region waiting for IO
|
|
PageVa = DmaIoPtr->Used.Ptr;
|
|
NoPages = (Length = DmaIoPtr->Used.Size) >> PAGE_SHIFT;
|
|
// Make sure all pages should be contiguous
|
|
i = DmaIoPtr->Used.Ptr - DmaIoPtr->Used.Beg;
|
|
ASSERT (((i | Length) & (PAGE_SIZE-1)) == 0);
|
|
i >>= PAGE_SHIFT;
|
|
|
|
// Starting file offset (in pages)
|
|
FilePage = DmaIoPtr->FilePage[i];
|
|
|
|
// Increase counter while contiguous and used
|
|
if (HIBER_USE_DMA (HiberContext)) {
|
|
// If DMA is allowed write page-by-page
|
|
j = 1;
|
|
} else {
|
|
// Write as many pages as possible
|
|
j = 0;
|
|
do {
|
|
++j;
|
|
} while ((j != NoPages) &&
|
|
(DmaIoPtr->FilePage[i + j] == FilePage + j));
|
|
}
|
|
|
|
// Re-evaluate # of pages and length of block
|
|
Length = (NoPages = j) << PAGE_SHIFT;
|
|
|
|
// Start IO
|
|
PopWriteHiberPages (HiberContext, PageVa, NoPages, FilePage, &DmaIoPtr->HiberWritePagesLocals);
|
|
if (!NT_SUCCESS (HiberContext->Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// If pending then return immediately (even if need to complete)
|
|
if (DmaIoPtr->Busy.Size != 0) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
PopIOWrite (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PUCHAR Ptr,
|
|
IN LONG Bytes,
|
|
IN PFN_NUMBER FilePage
|
|
)
|
|
{
|
|
LONG i, Size;
|
|
ULONGLONG StartCount;
|
|
|
|
// Do not bother if don't writing and/or was an error
|
|
if (!HiberContext->WriteToFile || !NT_SUCCESS(HiberContext->Status)) {
|
|
return;
|
|
}
|
|
|
|
ASSERT ((Bytes & (PAGE_SIZE-1)) == 0);
|
|
|
|
while (Bytes > 0) {
|
|
// Complete or Resume IO
|
|
do {
|
|
if (!PopIOResume (HiberContext, (BOOLEAN) (DmaIoPtr->Free.Size == 0))) {
|
|
return;
|
|
}
|
|
} while (DmaIoPtr->Free.Size == 0);
|
|
|
|
// Find how much can we write
|
|
Size = DmaIoPtr->Free.Size;
|
|
ASSERT ((Size & (PAGE_SIZE-1)) == 0);
|
|
if (Size > Bytes) {
|
|
Size = Bytes;
|
|
}
|
|
ASSERT (Size != 0);
|
|
// Copy and adjust pointers
|
|
|
|
HbCopy (HiberContext, DmaIoPtr->Free.Ptr, Ptr, Size);
|
|
|
|
Ptr += Size;
|
|
Bytes -= Size;
|
|
|
|
// Remember current page # index
|
|
i = (ULONG)(DmaIoPtr->Free.Ptr - DmaIoPtr->Free.Beg);
|
|
ASSERT ((i & (PAGE_SIZE-1)) == 0);
|
|
i >>= PAGE_SHIFT;
|
|
|
|
// Mark free memory as used
|
|
PopIORegionMove (&DmaIoPtr->Used, &DmaIoPtr->Free, Size);
|
|
|
|
// Remember FilePage for newly used pages
|
|
do {
|
|
DmaIoPtr->FilePage[i] = FilePage;
|
|
++i;
|
|
++FilePage;
|
|
} while ((Size -= PAGE_SIZE) != 0);
|
|
}
|
|
|
|
// Resume IO
|
|
PopIOResume (HiberContext, FALSE);
|
|
}
|
|
|
|
|
|
VOID
|
|
PopWriteHiberPages (
|
|
IN PPOP_HIBER_CONTEXT HiberContext,
|
|
IN PVOID ArgPageVa,
|
|
IN PFN_NUMBER ArgNoPages,
|
|
IN PFN_NUMBER ArgFilePage,
|
|
IN HIBER_WRITE_PAGES_LOCALS *Locals
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to write pages into the hibernation file.
|
|
Caller must map pages to virtual addresses.
|
|
|
|
Arguments:
|
|
|
|
HiberContext - The hibernation context structure
|
|
|
|
PageVa - Virtual address of the first page to write
|
|
|
|
NoPage - Number of consective pages to write
|
|
|
|
FilePage - Page address in hiber file to write this
|
|
run of pages.
|
|
|
|
PendingIOStatus - If NULL then pass IO request to PopIOWrite,
|
|
otherwise it's call from PopIOResume for delayed
|
|
IO; used to return # of bytes written and pending
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DUMP_MDL DumpMdl;
|
|
#define X(type,name) type name
|
|
HIBER_WRITE_PAGES_LOCALS_LIST (X)
|
|
#undef X
|
|
ULONGLONG StartCount, EndCount;
|
|
|
|
//
|
|
// Copy arguments to local variables
|
|
//
|
|
PageVa = ArgPageVa;
|
|
NoPages = ArgNoPages;
|
|
FilePage = ArgFilePage;
|
|
|
|
//
|
|
// Allow debugger to break in when we are hibernating.
|
|
//
|
|
|
|
KdCheckForDebugBreak ();
|
|
|
|
//
|
|
// If a file isn't being written, then ignore
|
|
//
|
|
|
|
if (!HiberContext->WriteToFile) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// If there's been some sort of error, don't bother
|
|
// writing anymore
|
|
//
|
|
|
|
if (!NT_SUCCESS(HiberContext->Status)) {
|
|
return ;
|
|
}
|
|
|
|
Mdl = (PMDL) DumpMdl;
|
|
if (Locals != NULL) {
|
|
// If we have async IO make sure that hand-made MDL will be
|
|
// stored in safe place preserved between resume calls
|
|
Mdl = (PMDL) Locals->DumpMdl;
|
|
|
|
if (DmaIoPtr->Busy.Size != 0) {
|
|
// There was pending IO -- resume execution from the point we stopped
|
|
#define X(type,name) name = Locals->name;
|
|
HIBER_WRITE_PAGES_LOCALS_LIST (X)
|
|
#undef X
|
|
goto ResumeIO;
|
|
}
|
|
|
|
// Mark current region as busy
|
|
ASSERT (PageVa == DmaIoPtr->Used.Ptr);
|
|
PopIORegionMove (&DmaIoPtr->Busy, &DmaIoPtr->Used, (ULONG)NoPages << PAGE_SHIFT);
|
|
} else if (HiberContext->DumpStack->Init.WritePendingRoutine != 0 &&
|
|
DmaIoPtr != NULL &&
|
|
DmaIoPtr->DumpLocalData != NULL) {
|
|
if (!DmaIoPtr->DmaInitialized) {
|
|
ULONGLONG StartCount = HIBER_GET_TICK_COUNT(NULL);
|
|
Status = HiberContext->DumpStack->Init.WritePendingRoutine (IO_DUMP_WRITE_INIT,
|
|
NULL,
|
|
NULL,
|
|
DmaIoPtr->DumpLocalData);
|
|
HiberContext->PerfInfo.InitTicks += HIBER_GET_TICK_COUNT(NULL) - StartCount;
|
|
if (Status != STATUS_SUCCESS) {
|
|
DmaIoPtr->UseDma = FALSE;
|
|
}
|
|
DmaIoPtr->DmaInitialized = TRUE;
|
|
DmaIoPtr->HiberWritePagesLocals.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
PopIOWrite (HiberContext, PageVa, (ULONG)NoPages << PAGE_SHIFT, FilePage);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Page count must be below 4GB byte length
|
|
//
|
|
|
|
if (NoPages > ((((ULONG_PTR) -1) << PAGE_SHIFT) >> PAGE_SHIFT)) {
|
|
PopInternalError (POP_HIBER);
|
|
}
|
|
|
|
//
|
|
// Loop while there's data to be written
|
|
//
|
|
|
|
CMcb = (PPOP_MCB_CONTEXT) HiberContext->CurrentMcb;
|
|
MdlPage = MmGetMdlPfnArray( Mdl );
|
|
|
|
FileBase = (ULONGLONG) FilePage << PAGE_SHIFT;
|
|
Length = NoPages << PAGE_SHIFT;
|
|
|
|
while (Length != 0) {
|
|
|
|
//
|
|
// If this IO is outside the current Mcb locate the
|
|
// proper Mcb
|
|
//
|
|
|
|
if (FileBase < CMcb->Base || FileBase >= CMcb->Base + CMcb->Mcb[0].QuadPart) {
|
|
|
|
//
|
|
// If io is before this mcb, search from the begining
|
|
//
|
|
|
|
if (FileBase < CMcb->Base) {
|
|
CMcb->Mcb = CMcb->FirstMcb;
|
|
CMcb->Base = 0;
|
|
}
|
|
|
|
//
|
|
// Find the Mcb which covers the start of the io and
|
|
// make it the current mcb
|
|
//
|
|
|
|
while (FileBase >= CMcb->Base + CMcb->Mcb[0].QuadPart) {
|
|
CMcb->Base += CMcb->Mcb[0].QuadPart;
|
|
CMcb->Mcb += 2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine physical IoLocation and IoLength to write.
|
|
//
|
|
|
|
McbOffset = FileBase - CMcb->Base;
|
|
IoLocation.QuadPart = CMcb->Mcb[1].QuadPart + McbOffset;
|
|
|
|
//
|
|
// If the IoLength is beyond the Mcb, limit it to the Mcb
|
|
//
|
|
|
|
if (McbOffset + Length > (ULONGLONG) CMcb->Mcb[0].QuadPart) {
|
|
IoLength = (ULONG) (CMcb->Mcb[0].QuadPart - McbOffset);
|
|
} else {
|
|
IoLength = (ULONG) Length;
|
|
}
|
|
|
|
//
|
|
// If the IoLength is more pages then the largest Mdl size
|
|
// then shrink it
|
|
//
|
|
|
|
NoPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (PageVa, IoLength);
|
|
if (NoPages > IO_DUMP_MAX_MDL_PAGES) {
|
|
IoLength -= (ULONG)((NoPages - IO_DUMP_MAX_MDL_PAGES) << PAGE_SHIFT);
|
|
NoPages = IO_DUMP_MAX_MDL_PAGES;
|
|
}
|
|
|
|
//
|
|
// Debugging only
|
|
// Make sure that we may handle non-page aligned IO
|
|
// (simulate fragmented hiberfil.sys)
|
|
//
|
|
// if (IoLength > 512) IoLength = 512;
|
|
//
|
|
|
|
if (HIBER_USE_DMA (HiberContext)) {
|
|
ULONG Size;
|
|
|
|
// Do not write accross page boundaries
|
|
// to avoid memory allocation that HAL may do;
|
|
// Because of MCB's partial IOs may be smaller than one page
|
|
|
|
Size = PAGE_SIZE - (ULONG)((ULONG_PTR)PageVa & (PAGE_SIZE - 1));
|
|
if (IoLength > Size) {
|
|
IoLength = Size;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the Mdl for the Io
|
|
//
|
|
|
|
MmInitializeMdl(Mdl, PageVa, IoLength);
|
|
Mdl->MappedSystemVa = PageVa;
|
|
Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
|
|
for (i=0; i < NoPages; i++) {
|
|
pa = MmGetPhysicalAddress((PVOID) (((ULONG_PTR)PageVa) + (i << PAGE_SHIFT)));
|
|
MdlPage[i] = (PFN_NUMBER) (pa.QuadPart >> PAGE_SHIFT);
|
|
}
|
|
|
|
//
|
|
// Write the data
|
|
//
|
|
|
|
StartCount = HIBER_GET_TICK_COUNT(NULL);
|
|
|
|
if (Locals != NULL && HIBER_USE_DMA (HiberContext)) {
|
|
Status = HiberContext->DumpStack->Init.WritePendingRoutine (IO_DUMP_WRITE_START,
|
|
&IoLocation,
|
|
Mdl,
|
|
DmaIoPtr->DumpLocalData);
|
|
|
|
if (Status != STATUS_PENDING && !NT_SUCCESS (Status)) {
|
|
DBGOUT (("WriteDMA returned bad status 0x%x -- will use PIO\n", Status));
|
|
DmaIoPtr->UseDma = FALSE;
|
|
goto RetryWithPIO;
|
|
}
|
|
} else {
|
|
RetryWithPIO:
|
|
Status = HiberContext->DumpStack->Init.WriteRoutine (&IoLocation, Mdl);
|
|
}
|
|
|
|
EndCount = HIBER_GET_TICK_COUNT(NULL);
|
|
HiberContext->PerfInfo.IoTicks += EndCount - StartCount;
|
|
|
|
//
|
|
// Keep track of the number of pages written, and dump device calls
|
|
// made for performance metric reasons
|
|
//
|
|
|
|
HiberContext->PerfInfo.PagesWritten += (ULONG)NoPages;
|
|
HiberContext->PerfInfo.DumpCount += 1;
|
|
|
|
//
|
|
// Io complete or will be complete
|
|
//
|
|
|
|
Length -= IoLength;
|
|
FileBase += IoLength;
|
|
PageVa = (PVOID) (((PUCHAR) PageVa) + IoLength);
|
|
|
|
// Check status
|
|
if (Locals != NULL) {
|
|
if (Status == STATUS_PENDING) {
|
|
#define X(type,name) Locals->name = name
|
|
HIBER_WRITE_PAGES_LOCALS_LIST (X)
|
|
#undef X
|
|
return;
|
|
ResumeIO:
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
HiberContext->Status = Status;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Locals != NULL) {
|
|
// Completed IO request -- mark region as free
|
|
ASSERT (PageVa == DmaIoPtr->Busy.Ptr + DmaIoPtr->Busy.Size);
|
|
PopIORegionMove (&DmaIoPtr->Free, &DmaIoPtr->Busy, DmaIoPtr->Busy.Size);
|
|
}
|
|
}
|
|
|
|
|
|
UCHAR
|
|
PopGetHiberFlags(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines any hibernation flags which need to be written
|
|
into the hiber image and made visible to the osloader at
|
|
resume time
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
UCHAR containing hibernation flags. Currently defined flags:
|
|
PO_HIBER_APM_RECONNECT
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR Flags=0;
|
|
HANDLE ApmActiveKey;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING Name;
|
|
NTSTATUS Status;
|
|
PULONG ApmActive;
|
|
UCHAR ValueBuff[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
|
|
ULONG ResultLength;
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuff;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if defined(i386)
|
|
//
|
|
// Open the APM active key to determine if APM is running.
|
|
//
|
|
RtlInitUnicodeString(&Name, PopApmActiveFlag);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&Name,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
Status = ZwOpenKey(&ApmActiveKey,
|
|
KEY_READ,
|
|
&ObjectAttributes);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Query the Active value. A value of 1 indicates that APM is running.
|
|
//
|
|
RtlInitUnicodeString(&Name, PopApmFlag);
|
|
Status = ZwQueryValueKey(ApmActiveKey,
|
|
&Name,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
sizeof(ValueBuff),
|
|
&ResultLength);
|
|
ZwClose(ApmActiveKey);
|
|
if (NT_SUCCESS(Status) && (ValueInfo->Type == REG_DWORD)) {
|
|
ApmActive = (PULONG)&ValueInfo->Data;
|
|
if (*ApmActive == 1) {
|
|
Flags |= PO_HIBER_APM_RECONNECT;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return(Flags);
|
|
}
|
|
|
|
|
|
PMDL
|
|
PopSplitMdl(
|
|
IN PMDL Original,
|
|
IN ULONG SplitPages
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Splits a new MDL of length SplitPages out from the original MDL.
|
|
This is needed so that when we have an enormous MDL of spare pages
|
|
we do not have to map the whole thing, just the part we need.
|
|
|
|
Arguments:
|
|
|
|
Original - supplies the original MDL. The length of this MDL will
|
|
be decreated by SplitPages
|
|
|
|
SplitPages - supplies the length (in pages) of the new MDL.
|
|
|
|
Return Value:
|
|
|
|
pointer to newly allocated MDL
|
|
NULL if a new MDL could not be allocated
|
|
|
|
--*/
|
|
|
|
{
|
|
PMDL NewMdl;
|
|
ULONG Length;
|
|
PPFN_NUMBER SourcePages;
|
|
PPFN_NUMBER DestPages;
|
|
|
|
Length = SplitPages << PAGE_SHIFT;
|
|
|
|
NewMdl = ExAllocatePoolWithTag(NonPagedPool,
|
|
MmSizeOfMdl(NULL, Length),
|
|
POP_HMAP_TAG);
|
|
if (NewMdl == NULL) {
|
|
return(NULL);
|
|
}
|
|
MmInitializeMdl(NewMdl, NULL, Length);
|
|
DestPages = (PPFN_NUMBER)(NewMdl + 1);
|
|
SourcePages = (PPFN_NUMBER)(Original + 1) + BYTES_TO_PAGES(Original->ByteCount) - SplitPages;
|
|
RtlCopyMemory(DestPages, SourcePages, SplitPages * sizeof(PFN_NUMBER));
|
|
Original->ByteCount -= (SplitPages << PAGE_SIZE);
|
|
|
|
return(NewMdl);
|
|
}
|