windows-nt/Source/XPSP1/NT/base/ntos/po/hiber.c

4886 lines
138 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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);
}