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

2405 lines
68 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
hiber.c
Abstract:
Author:
Revision History:
8/7/1998 Elliot Shmukler (t-ellios) Added Hiber file compression
--*/
#include "bldr.h"
#include "msg.h"
#include "stdio.h"
#include "stdlib.h"
#include "xpress.h"
extern UCHAR WakeDispatcherStart;
extern UCHAR WakeDispatcherEnd;
#if defined(_X86_)
extern UCHAR WakeDispatcherAmd64Start;
extern UCHAR WakeDispatcherAmd64End;
#endif
//
//
// Hiber globals
//
// HiberFile - File handle
// HiberBuffer - PAGE of ram
// HiberIoError - Set to true to indicate an IO read error occured during restore
//
ULONG HiberFile;
PUCHAR HiberBuffer;
ULONG HiberBufferPage;
BOOLEAN HiberIoError;
BOOLEAN HiberOutOfRemap;
BOOLEAN HiberAbort;
LARGE_INTEGER HiberStartTime;
LARGE_INTEGER HiberEndTime;
ULONG HiberNoExecute = 0;
//
// HiberImageFeatureFlags - Feature flags from hiber image header
// HiberBreakOnWake - BreakOnWake flag from hiber image header
//
BOOLEAN HiberBreakOnWake;
ULONG HiberImageFeatureFlags;
#if defined(_ALPHA_) || defined(_IA64_)
//
// On Alpha, the address of the KPROCESSOR_STATE read from the hiber file
// must be saved where WakeDispatch can find it (it's at a fixed offset
// relative to HiberVa on x86).
//
PKPROCESSOR_STATE HiberWakeState;
#else // x86
//
// HiberPtes - Virtual address of ptes to use for restoriation. There
// are at least HIBER_PTES consecutive ptes for use, and are for the
// address of HiberVa
//
// HiberVa - The virtual address the HiberPtes map
//
// HiberIdentityVa - The restoration images HiberVa
//
// HiberPageFrames - Page frames of the hiber ptes (does not include dest pte)
//
PVOID HiberPtes = NULL;
PUCHAR HiberVa = NULL;
PVOID HiberIdentityVa = NULL;
ULONG64 HiberIdentityVaAmd64 = 0;
ULONG HiberNoHiberPtes;
ULONG HiberPageFrames[HIBER_PTES];
#endif // Alpha/x86
PFN_NUMBER HiberImagePageSelf;
ULONG HiberNoMappings;
ULONG HiberFirstRemap;
ULONG HiberLastRemap;
extern
ULONG
BlGetKey(
VOID
);
extern
ULONG
BlDetermineOSVisibleMemory(
VOID
);
VOID
BlUpdateProgressBar(
ULONG fPercentage
);
VOID
BlOutputStartupMsg(
ULONG uMsgID
);
VOID
BlOutputTrailerMsg(
ULONG uMsgID
);
//
// Defines for Hiber restore UI
//
ULONG HbCurrentScreen;
#define BAR_X 7
#define BAR_Y 10
#define PERCENT_BAR_WIDTH 66
#define PAUSE_X 7
#define PAUSE_Y 7
#define FAULT_X 7
#define FAULT_Y 7
UCHAR szHiberDebug[] = "debug";
UCHAR szHiberFileName[] = "\\hiberfil.sys";
//
// HiberFile Compression Related definnes
//
#define PAGE_MASK (PAGE_SIZE - 1)
#define PAGE_PAGES(n) (((n) + PAGE_MASK) >> PAGE_SHIFT)
//
// The size of the buffer for compressed data
#define COMPRESSION_BUFFER_SIZE 64 << PAGE_SHIFT
//
#define MAX_COMPRESSION_BUFFER_EXTRA_PAGES \
PAGE_PAGES (PAGE_MASK + 2*XPRESS_HEADER_SIZE)
#define MAX_COMPRESSION_BUFFER_EXTRA_SIZE \
(MAX_COMPRESSION_BUFFER_EXTRA_PAGES << PAGE_SHIFT)
#define LZNT1_COMPRESSION_BUFFER_PAGES 16
#define LZNT1_COMPRESSION_BUFFER_SIZE \
(LZNT1_COMPRESSION_BUFFER_PAGES << PAGE_SHIFT)
#define XPRESS_COMPRESSION_BUFFER_PAGES \
PAGE_PAGES (XPRESS_MAX_SIZE + MAX_COMPRESSION_BUFFER_EXTRA_SIZE)
#define XPRESS_COMPRESSION_BUFFER_SIZE \
(XPRESS_COMPRESSION_BUFFER_PAGES << PAGE_SHIFT)
#define MAX_COMPRESSION_BUFFER_PAGES \
max (LZNT1_COMPRESSION_BUFFER_PAGES, XPRESS_COMPRESSION_BUFFER_PAGES)
#define MAX_COMPRESSION_BUFFER_SIZE \
(MAX_COMPRESSION_BUFFER_PAGES << PAGE_SHIFT)
// Buffer to store decoded data
typedef struct {
PUCHAR DataPtr, PreallocatedDataBuffer;
LONG DataSize;
struct {
struct {
LONG Size;
ULONG Checksum;
} Compressed, Uncompressed;
LONG XpressEncoded;
} Header;
LONG DelayedCnt; // # of delayed pages
ULONG DelayedChecksum; // last checksum value
ULONG DelayedBadChecksum;
struct {
PUCHAR DestVa; // delayed DestVa
PFN_NUMBER DestPage;// delayed page number
ULONG RangeCheck; // last range checksum
LONG Flags; // 1 = clear checksum, 2 = compare checksum
} Delayed[XPRESS_MAX_PAGES];
} DECOMPRESSED_BLOCK, *PDECOMPRESSED_BLOCK;
typedef struct {
struct {
PUCHAR Beg;
PUCHAR End;
} Current, Buffer, Aligned;
PFN_NUMBER FilePage;
BOOLEAN NeedSeek;
} COMPRESSED_BUFFER, *PCOMPRESSED_BUFFER;
#define HIBER_PERF_STATS 0
//
// Internal prototypes
//
#if !defined (HIBER_DEBUG)
#define CHECK_ERROR(a,b) if(a) { *Information = __LINE__; return b; }
#define DBGOUT(_x_)
#else
#define CHECK_ERROR(a,b) if(a) {HbPrintMsg(b);HbPrint(TEXT("\r\n")); *Information = __LINE__; HbPause(); return b; }
#define DBGOUT(_x_) BlPrint _x_
#endif
ULONG
HbRestoreFile (
IN PULONG Information,
OUT OPTIONAL PCHAR *BadLinkName
);
VOID
HbPrint (
IN PTCHAR str
);
BOOLEAN
HbReadNextCompressedPageLZNT1 (
PUCHAR DestVa,
PCOMPRESSED_BUFFER CompressedBuffer
);
BOOLEAN
HbReadNextCompressedChunkLZNT1 (
PUCHAR DestVa,
PCOMPRESSED_BUFFER CompressedBuffer
);
BOOLEAN
HbReadNextCompressedPages (
LONG BytesNeeded,
PCOMPRESSED_BUFFER CompressedBuffer
);
BOOLEAN
HbReadNextCompressedBlock (
PDECOMPRESSED_BLOCK Block,
PCOMPRESSED_BUFFER CompressedBuffer
);
BOOLEAN
HbReadDelayedBlock (
BOOLEAN ForceDecoding,
PFN_NUMBER DestPage,
ULONG RangeCheck,
PDECOMPRESSED_BLOCK Block,
PCOMPRESSED_BUFFER CompressedBuffer
);
BOOLEAN
HbReadNextCompressedBlockHeader (
PDECOMPRESSED_BLOCK Block,
PCOMPRESSED_BUFFER CompressedBuffer
);
ULONG
BlHiberRestore (
IN ULONG DriveId,
OUT PCHAR *BadLinkName
);
BOOLEAN
HbReadNextCompressedChunk (
PUCHAR DestVa,
PPFN_NUMBER FilePage,
PUCHAR CompressBuffer,
PULONG DataOffset,
PULONG BufferOffset,
ULONG MaxOffset
);
#if defined (HIBER_DEBUG) || HIBER_PERF_STATS
// HIBER_DEBUG bit mask:
// 2 - general bogosity
// 4 - remap trace
VOID HbFlowControl(VOID)
{
UCHAR c;
ULONG count;
if (ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) {
ArcRead(ARC_CONSOLE_INPUT, &c, 1, &count);
if (c == 'S' - 0x40) {
ArcRead(ARC_CONSOLE_INPUT, &c, 1, &count);
}
}
}
VOID HbPause(VOID)
{
UCHAR c;
ULONG count;
#if defined(ENABLE_LOADER_DEBUG)
DbgBreakPoint();
#else
HbPrint(TEXT("Press any key to continue . . ."));
ArcRead(ARC_CONSOLE_INPUT, &c, 1, &count);
HbPrint(TEXT("\r\n"));
#endif
}
VOID HbPrintNum(ULONG n)
{
TCHAR buf[9];
_stprintf(buf, TEXT("%ld"), n);
HbPrint(buf);
HbFlowControl();
}
VOID HbPrintHex(ULONG n)
{
TCHAR buf[11];
_stprintf(buf, TEXT("0x%08lX"), n);
HbPrint(buf);
HbFlowControl();
}
#define SHOWNUM(x) ((void) (HbPrint(#x TEXT(" = ")), HbPrintNum((ULONG) (x)), HbPrint(TEXT("\r\n"))))
#define SHOWHEX(x) ((void) (HbPrint(#x TEXT(" = ")), HbPrintHex((ULONG) (x)), HbPrint(TEXT("\r\n"))))
#endif // HIBER_DEBUG
#if !defined(i386) && !defined(_ALPHA_)
ULONG
HbSimpleCheck (
IN ULONG PartialSum,
IN PVOID SourceVa,
IN ULONG Length
);
#else
// Use the TCP/IP Check Sum routine if available
ULONG
tcpxsum(
IN ULONG cksum,
IN PUCHAR buf,
IN ULONG len
);
#define HbSimpleCheck(a,b,c) tcpxsum(a,(PUCHAR)b,c)
#endif
//
// The macros below helps to access 64-bit structures of Amd64 from
// their 32-bit definitions in loader. If the hiber image is not
// for Amd64, these macros simply reference structure fields directly.
//
//
// Define macros to read a field in a structure.
//
// READ_FIELD (struct_type, struct_ptr, field, field_type)
//
// Areguments:
//
// struct_type - type of the structure
//
// struct_ptr - base address of the structure
//
// field - field name
//
// field_type - data type of the field
//
#if defined(_X86_)
#define READ_FIELD(struct_type, struct_ptr, field, field_type) \
(BlAmd64UseLongMode ? \
*((field_type *)((ULONG_PTR)struct_ptr + \
BlAmd64FieldOffset_##struct_type(FIELD_OFFSET(struct_type, field)))) : \
(field_type)(((struct_type *)(struct_ptr))->field))
#else
#define READ_FIELD(struct_type, struct_ptr, field, field_type) \
(field_type)(((struct_type *)(struct_ptr))->field)
#define WRITE_FIELD(struct_type, struct_ptr, field, field_type, data) \
(((struct_type *)(struct_ptr))->field) = (field_type)data;
#endif
#define READ_FIELD_UCHAR(struct_type, struct_ptr, field) \
READ_FIELD(struct_type, struct_ptr, field, UCHAR)
#define READ_FIELD_ULONG(struct_type, struct_ptr, field) \
READ_FIELD(struct_type, struct_ptr, field, ULONG) \
#define READ_FIELD_ULONG64(struct_type, struct_ptr, field) \
READ_FIELD(struct_type, struct_ptr, field, ULONG64)
//
// Here we assume the high dword of a 64-bit pfn on Amd64 is zero.
// Otherwise hibernation should be disabled.
//
#define READ_FIELD_PFN_NUMBER(struct_type, struct_ptr, field) \
READ_FIELD(struct_type, struct_ptr, field, PFN_NUMBER) \
//
// Define macros to write a field in a structure.
//
// WRITE_FIELD (struct_type, struct_ptr, field, field_type, data)
//
// Areguments:
//
// struct_type - type of the structure
//
// struct_ptr - base address of the structure
//
// field - field name
//
// field_type - data type of the field
//
// data - value to be set to the field
//
#if defined(_X86_)
#define WRITE_FIELD(struct_type, struct_ptr, field, field_type, data) \
if(BlAmd64UseLongMode) { \
*((field_type *)((ULONG_PTR)struct_ptr + \
BlAmd64FieldOffset_##struct_type(FIELD_OFFSET(struct_type, field)))) = (field_type)data; \
} else { \
(((struct_type *)(struct_ptr))->field) = (field_type)data; \
}
#else
#define WRITE_FIELD(struct_type, struct_ptr, field, field_type, data) \
(((struct_type *)(struct_ptr))->field) = (field_type)data;
#endif
#define WRITE_FIELD_ULONG(struct_type, struct_ptr, field, data) \
WRITE_FIELD(struct_type, struct_ptr, field, ULONG, data)
//
// Define macros to read a field of a element in a structure array.
//
// READ_ELEMENT_FIELD(struct_type, array, index, field, field_type)
//
// Areguments:
//
// struct_type - type of the structure
//
// array - base address of the array
//
// index - index of the element
//
// field - field name
//
// field_type - data type of the field
//
#if defined(_X86_)
#define ELEMENT_OFFSET(type, index) \
(BlAmd64UseLongMode ? BlAmd64ElementOffset_##type(index): \
(ULONG)(&(((type *)0)[index])))
#define READ_ELEMENT_FIELD(struct_type, array, index, field, field_type) \
READ_FIELD(struct_type, ((PUCHAR)array + ELEMENT_OFFSET(struct_type, index)), field, field_type)
#else
#define READ_ELEMENT_FIELD(struct_type, array, index, field, field_type) \
(field_type)(((struct_type *)array)[index].field)
#endif
#define READ_ELEMENT_FIELD_ULONG(struct_type, array, index, field) \
READ_ELEMENT_FIELD(struct_type, array, index, field, ULONG)
//
// Here we assume the high dword of a 64-bit pfn on Amd64 is zero.
// Otherwise hibernation should be disabled.
//
#define READ_ELEMENT_FIELD_PFN_NUMBER(struct_type, array, index, field) \
READ_ELEMENT_FIELD(struct_type, array, index, field, PFN_NUMBER)
VOID
HbReadPage (
IN PFN_NUMBER PageNo,
IN PUCHAR Buffer
);
VOID
HbSetImageSignature (
IN ULONG NewSignature
);
VOID
HbPrint (
IN PTCHAR str
)
{
ULONG Junk;
ArcWrite (
BlConsoleOutDeviceId,
str,
(ULONG)_tcslen(str)*sizeof(TCHAR),
&Junk
);
}
VOID HbPrintChar (_TUCHAR chr)
{
ULONG Junk;
ArcWrite(
BlConsoleOutDeviceId,
&chr,
sizeof(_TUCHAR),
&Junk
);
}
VOID
HbPrintMsg (
IN ULONG MsgNo
)
{
PTCHAR Str;
Str = BlFindMessage(MsgNo);
if (Str) {
HbPrint (Str);
}
}
VOID
HbScreen (
IN ULONG Screen
)
{
#if defined(HIBER_DEBUG)
HbPrint(TEXT("\r\n"));
HbPause();
#endif
HbCurrentScreen = Screen;
BlSetInverseMode (FALSE);
BlPositionCursor (1, 1);
BlClearToEndOfScreen();
BlPositionCursor (1, 3);
HbPrintMsg(Screen);
}
ULONG
HbSelection (
ULONG x,
ULONG y,
PULONG Sel,
ULONG Debug
)
{
ULONG CurSel, MaxSel;
ULONG i;
UCHAR Key;
PUCHAR pDebug;
for (MaxSel=0; Sel[MaxSel]; MaxSel++) ;
MaxSel -= Debug;
pDebug = szHiberDebug;
#if DBG
MaxSel += Debug;
Debug = 0;
#endif
CurSel = 0;
for (; ;) {
//
// Draw selections
//
for (i=0; i < MaxSel; i++) {
BlPositionCursor (x, y+i);
BlSetInverseMode ((BOOLEAN) (CurSel == i) );
HbPrintMsg(Sel[i]);
}
//
// Get a key
//
ArcRead(ARC_CONSOLE_INPUT, &Key, sizeof(Key), &i);
if (Key == ASCI_CSI_IN) {
ArcRead(ARC_CONSOLE_INPUT, &Key, sizeof(Key), &i);
switch (Key) {
case 'A':
//
// Cursor up
//
CurSel -= 1;
if (CurSel >= MaxSel) {
CurSel = MaxSel-1;
}
break;
case 'B':
//
// Cursor down
//
CurSel += 1;
if (CurSel >= MaxSel) {
CurSel = 0;
}
break;
}
} else {
if (Key == *pDebug) {
pDebug++;
if (!*pDebug) {
MaxSel += Debug;
Debug = 0;
}
} else {
pDebug = szHiberDebug;
}
switch (Key) {
case ASCII_LF:
case ASCII_CR:
BlSetInverseMode (FALSE);
BlPositionCursor (1, 2);
BlClearToEndOfScreen ();
if (Sel[CurSel] == HIBER_DEBUG_BREAK_ON_WAKE) {
HiberBreakOnWake = TRUE;
}
return CurSel;
}
}
}
}
VOID
HbCheckForPause (
VOID
)
{
ULONG uSel = 0;
UCHAR Key;
ULONG Sel[4];
BOOLEAN bPaused = FALSE;
//
// Check for space bar
//
if (ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) {
ArcRead(ARC_CONSOLE_INPUT, &Key, sizeof(Key), &uSel);
switch (Key) {
// space bar pressed
case ' ':
bPaused = TRUE;
break;
// user pressed F5/F8 key
case ASCI_CSI_IN:
ArcRead(ARC_CONSOLE_INPUT, &Key, sizeof(Key), &uSel);
if(Key == 'O') {
ArcRead(ARC_CONSOLE_INPUT, &Key, sizeof(Key), &uSel);
bPaused = (Key == 'r' || Key == 't');
}
break;
default:
bPaused = FALSE;
break;
}
if (bPaused) {
Sel[0] = HIBER_CONTINUE;
Sel[1] = HIBER_CANCEL;
Sel[2] = HIBER_DEBUG_BREAK_ON_WAKE;
Sel[3] = 0;
HbScreen(HIBER_PAUSE);
uSel = HbSelection (PAUSE_X, PAUSE_Y, Sel, 1);
if (uSel == 1) {
HiberIoError = TRUE;
HiberAbort = TRUE;
return ;
} else {
BlSetInverseMode(FALSE);
//
// restore hiber progress screen
//
BlOutputStartupMsg(BL_MSG_RESUMING_WINDOWS);
BlOutputTrailerMsg(BL_ADVANCED_BOOT_MESSAGE);
}
}
}
}
ULONG
BlHiberRestore (
IN ULONG DriveId,
OUT PCHAR *BadLinkName
)
/*++
Routine Description:
Checks DriveId for a valid hiberfile.sys and if found start the
restoration procedure
--*/
{
extern BOOLEAN BlOutputDots;
NTSTATUS Status;
ULONG Msg = 0;
ULONG Information;
ULONG Sel[2];
BOOLEAN bDots = BlOutputDots;
//
// If restore was aborted once, don't bother
//
#if defined (HIBER_DEBUG)
HbPrint(TEXT("BlHiberRestore\r\n"));
#endif
if (HiberAbort) {
return ESUCCESS;
}
//
// Get the hiber image. If not present, done.
//
Status = BlOpen (DriveId, (PCHAR)szHiberFileName, ArcOpenReadWrite, &HiberFile);
if (Status != ESUCCESS) {
#if defined (HIBER_DEBUG)
HbPrint(TEXT("No hiber image file.\r\n"));
#endif
return ESUCCESS;
}
//
// Restore the hiber image
//
BlOutputDots = TRUE;
//
// Set the global flag to allow blmemory.c to grab from the right
// part of the buffer
//
BlRestoring=TRUE;
Msg = HbRestoreFile (&Information, BadLinkName);
BlOutputDots = bDots;
if (Msg) {
BlSetInverseMode (FALSE);
if (!HiberAbort) {
HbScreen(HIBER_ERROR);
HbPrintMsg(Msg);
Sel[0] = HIBER_CANCEL;
Sel[1] = 0;
HbSelection (FAULT_X, FAULT_Y, Sel, 0);
}
HbSetImageSignature (0);
}
BlClose (HiberFile);
BlRestoring=FALSE;
return Msg ? EAGAIN : ESUCCESS;
}
#if !defined(i386) && !defined(_ALPHA_)
ULONG
HbSimpleCheck (
IN ULONG PartialSum,
IN PVOID SourceVa,
IN ULONG Length
)
/*++
Routine Description:
Computes a checksum for the supplied virtual address and length
This function comes from Dr. Dobbs Journal, May 1992
--*/
{
PUSHORT Source;
Source = (PUSHORT) SourceVa;
Length = Length / 2;
while (Length--) {
PartialSum += *Source++;
PartialSum = (PartialSum >> 16) + (PartialSum & 0xFFFF);
}
return PartialSum;
}
#endif // i386
VOID
HbReadPage (
IN PFN_NUMBER PageNo,
IN PUCHAR Buffer
)
/*++
Routine Description:
This function reads the specified page from the hibernation file
Arguments:
PageNo - Page number to read
Buffer - Buffer to read the data
Return Value:
On success Buffer, else HbIoError set to TRUE
--*/
{
ULONG Status;
ULONG Count;
LARGE_INTEGER li;
li.QuadPart = (ULONGLONG) PageNo << PAGE_SHIFT;
Status = BlSeek (HiberFile, &li, SeekAbsolute);
if (Status != ESUCCESS) {
HiberIoError = TRUE;
}
Status = BlRead (HiberFile, Buffer, PAGE_SIZE, &Count);
if (Status != ESUCCESS) {
HiberIoError = TRUE;
}
}
BOOLEAN
HbReadNextCompressedPages (
LONG BytesNeeded,
PCOMPRESSED_BUFFER CompressedBuffer
)
/*++
Routine Description:
This routine makes sure that BytesNeeded bytes are available
in CompressedBuffer and brings in more pages from Hiber file
if necessary.
All reads from the Hiber file occurr at the file's
current offset forcing compressed pages to be read
in a continuous fashion without extraneous file seeks.
Arguments:
BytesNeeded - Number of bytes that must be present in CompressedBuffer
CompressedBuffer - Descriptor of data already brought in
Return Value:
TRUE if the operation is successful, FALSE otherwise.
--*/
{
LONG BytesLeft;
LONG BytesRequested;
ULONG Status;
LONG MaxBytes;
// Obtain number of bytes left in buffer
BytesLeft = (LONG) (CompressedBuffer->Current.End - CompressedBuffer->Current.Beg);
// Obtain number of bytes that are needed but not available
BytesNeeded -= BytesLeft;
// Preserve amount of bytes caller needs (BytesNeeded may be changed later)
BytesRequested = BytesNeeded;
// Do we need to read more?
if (BytesNeeded <= 0) {
// No, do nothing
return(TRUE);
}
// Align BytesNeeded on page boundary
BytesNeeded = (BytesNeeded + PAGE_MASK) & ~PAGE_MASK;
// Copy left bytes to the beginning of aligned buffer retaining page alignment
if (BytesLeft == 0) {
CompressedBuffer->Current.Beg = CompressedBuffer->Current.End = CompressedBuffer->Aligned.Beg;
} else {
LONG BytesBeforeBuffer = (LONG)(CompressedBuffer->Aligned.Beg - CompressedBuffer->Buffer.Beg) & ~PAGE_MASK;
LONG BytesLeftAligned = (BytesLeft + PAGE_MASK) & ~PAGE_MASK;
LONG BytesToCopy;
PUCHAR Dst, Src;
// Find out how many pages we may keep before aligned buffer
if (BytesBeforeBuffer >= BytesLeftAligned) {
BytesBeforeBuffer = BytesLeftAligned;
}
// Avoid misaligned data accesses during copy
BytesToCopy = (BytesLeft + 63) & ~63;
Dst = CompressedBuffer->Aligned.Beg + BytesLeftAligned - BytesBeforeBuffer - BytesToCopy;
Src = CompressedBuffer->Current.End - BytesToCopy;
if (Dst != Src) {
RtlMoveMemory (Dst, Src, BytesToCopy);
BytesLeftAligned = (LONG) (Dst - Src);
CompressedBuffer->Current.Beg += BytesLeftAligned;
CompressedBuffer->Current.End += BytesLeftAligned;
}
}
//
// Increase the number of bytes read to fill our buffer up to the next
// 64K boundary.
//
MaxBytes = (LONG)((((ULONG_PTR)CompressedBuffer->Current.End + 0x10000) & 0xffff) - (ULONG_PTR)CompressedBuffer->Current.End);
if (MaxBytes > CompressedBuffer->Buffer.End - CompressedBuffer->Current.End) {
MaxBytes = (LONG)(CompressedBuffer->Buffer.End - CompressedBuffer->Current.End);
}
if (MaxBytes > BytesNeeded) {
BytesNeeded = MaxBytes;
}
#if 0
// for debugging only
if (0x10000 - (((LONG) CompressedBuffer->Current.End) & 0xffff) < BytesNeeded) {
BlPrint (("Current.Beg = %p, Current.End = %p, Current.End2 = %p\n",
CompressedBuffer->Current.Beg,
CompressedBuffer->Current.End,
CompressedBuffer->Current.End + BytesNeeded
));
}
#endif
// Make sure we have enough space
if (BytesNeeded > CompressedBuffer->Buffer.End - CompressedBuffer->Current.End) {
// Too many bytes to read -- should never happen, but just in case...
DBGOUT (("Too many bytes to read -- corrupted data?\n"));
return(FALSE);
}
// Issue seek if necessary
if (CompressedBuffer->NeedSeek) {
LARGE_INTEGER li;
li.QuadPart = (ULONGLONG) CompressedBuffer->FilePage << PAGE_SHIFT;
Status = BlSeek (HiberFile, &li, SeekAbsolute);
if (Status != ESUCCESS) {
DBGOUT (("Seek to 0x%x error 0x%x\n", CompressedBuffer->FilePage, Status));
HiberIoError = TRUE;
return(FALSE);
}
CompressedBuffer->NeedSeek = FALSE;
}
// Read in stuff from the Hiber file into the available buffer space
Status = BlRead (HiberFile, CompressedBuffer->Current.End, BytesNeeded, (PULONG)&BytesNeeded);
// Check for I/O errors...
if (Status != ESUCCESS || ((ULONG)BytesNeeded & PAGE_MASK) != 0 || (BytesNeeded < BytesRequested)) {
// I/O Error - FAIL.
DBGOUT (("Read error: Status = 0x%x, ReadBytes = 0x%x, Requested = 0x%x\n", Status, BytesNeeded, BytesRequested));
HiberIoError = TRUE;
return(FALSE);
}
// I/O was good - recalculate buffer offsets based on how much
// stuff was actually read in
CompressedBuffer->Current.End += (ULONG)BytesNeeded;
CompressedBuffer->FilePage += ((ULONG)BytesNeeded >> PAGE_SHIFT);
return(TRUE);
}
BOOLEAN
HbReadNextCompressedBlockHeader (
PDECOMPRESSED_BLOCK Block,
PCOMPRESSED_BUFFER CompressedBuffer
)
/*++
Routine Description:
Read next compressed block header if it's Xpress compression.
Arguments:
Block - Descriptor of compressed data block
CompressedBuffer - Descriptor of data already brought in
Return Value:
TRUE if block is not Xpress block at all or valid Xpress block, FALSE otherwise
--*/
{
PUCHAR Buffer;
LONG CompressedSize; // they all must be signed -- do not change to ULONG
LONG UncompressedSize;
ULONG PackedSizes;
// First make sure next compressed data block header is available
if (!HbReadNextCompressedPages (XPRESS_HEADER_SIZE, CompressedBuffer)) {
// I/O error or bad header -- FAIL
return(FALSE);
}
// Set pointer to the beginning of buffer
Buffer = CompressedBuffer->Current.Beg;
// Check header magic
Block->Header.XpressEncoded = (RtlCompareMemory (Buffer, XPRESS_HEADER_STRING, XPRESS_HEADER_STRING_SIZE) == XPRESS_HEADER_STRING_SIZE);
if (!Block->Header.XpressEncoded) {
// Not Xpress -- return OK
return(TRUE);
}
// Skip magic string -- we will not need it anymore
Buffer += XPRESS_HEADER_STRING_SIZE;
// Read sizes of compressed and uncompressed data
PackedSizes = Buffer[0] + (Buffer[1] << 8) + (Buffer[2] << 16) + (Buffer[3] << 24);
CompressedSize = (LONG) (PackedSizes >> 10) + 1;
UncompressedSize = ((LONG) (PackedSizes & 1023) + 1) << PAGE_SHIFT;
Block->Header.Compressed.Size = CompressedSize;
Block->Header.Uncompressed.Size = UncompressedSize;
// Read checksums
Block->Header.Uncompressed.Checksum = Buffer[4] + (Buffer[5] << 8);
Block->Header.Compressed.Checksum = Buffer[6] + (Buffer[7] << 8);
// Clear space occupied by compressed checksum
Buffer[6] = Buffer[7] = 0;
// Make sure sizes are in correct range
if (UncompressedSize > XPRESS_MAX_SIZE ||
CompressedSize > UncompressedSize ||
CompressedSize == 0 ||
UncompressedSize == 0) {
// broken input data -- do not even try to decompress
DBGOUT (("Corrupted header: %02x %02x %02x %02x %02x %02x %02x %02x\n",
Buffer[0], Buffer[1], Buffer[2], Buffer[3], Buffer[4], Buffer[5], Buffer[6], Buffer[7]));
DBGOUT (("CompressedSize = %d, UncompressedSize = %d\n", CompressedSize, UncompressedSize));
return(FALSE);
}
// Xpress header and it looks OK so far
return(TRUE);
}
BOOLEAN
HbReadNextCompressedBlock (
PDECOMPRESSED_BLOCK Block,
PCOMPRESSED_BUFFER CompressedBuffer
)
/*++
Routine Description:
Reads and decompresses the next compressed chunk from the Hiber file
and stores it in a designated region of virtual memory.
Since no master data structure exists within the Hiber file to identify
the location of all of the compression chunks, this routine operates
by reading sections of the Hiber file into a compression buffer
and extracting chunks from that buffer.
Chunks are extracted by determining if a chunk is completely present in the buffer
using the RtlDescribeChunk API. If the chunk is not completely present,
more of the Hiber file is read into the buffer until the chunk can
be extracted.
All reads from the Hiber file occurr at its current offset, forcing
compressed chunks to be read in a continous fashion with no extraneous
seeks.
Arguments:
Block - Descriptor of compressed data block
CompressedBuffer - Descriptor of data already brought in
Return Value:
TRUE if a chunk has been succesfully extracted and decompressed, FALSE otherwise.
--*/
{
PUCHAR Buffer;
LONG CompressedSize; // they all must be signed -- do not change to ULONG
LONG AlignedCompressedSize;
LONG UncompressedSize;
// First make sure next compressed data block header is available
if (!HbReadNextCompressedBlockHeader (Block, CompressedBuffer)) {
// I/O error -- FAIL
return(FALSE);
}
// It must be Xpress
if (!Block->Header.XpressEncoded) {
#ifdef HIBER_DEBUG
// Set pointer to the beginning of buffer
Buffer = CompressedBuffer->Current.Beg;
// wrong magic -- corrupted data
DBGOUT (("Corrupted header: %02x %02x %02x %02x %02x %02x %02x %02x\n",
Buffer[0], Buffer[1], Buffer[2], Buffer[3], Buffer[4], Buffer[5], Buffer[6], Buffer[7]));
#endif /* HIBER_DEBUG */
return(FALSE);
}
// Read sizes
UncompressedSize = Block->Header.Uncompressed.Size;
CompressedSize = Block->Header.Compressed.Size;
// If not enough space supplied use preallocated buffer
if (UncompressedSize != Block->DataSize) {
Block->DataSize = UncompressedSize;
Block->DataPtr = Block->PreallocatedDataBuffer;
}
// Evaluate aligned size of compressed data
AlignedCompressedSize = (CompressedSize + (XPRESS_ALIGNMENT - 1)) & ~(XPRESS_ALIGNMENT - 1);
// Make sure we have all compressed data and the header in buffer
if (!HbReadNextCompressedPages (AlignedCompressedSize + XPRESS_HEADER_SIZE, CompressedBuffer)) {
// I/O error -- FAIL
return(FALSE);
}
// Set pointer to the beginning of buffer
Buffer = CompressedBuffer->Current.Beg;
// We will use some bytes out of buffer now -- reflect this fact
CompressedBuffer->Current.Beg += AlignedCompressedSize + XPRESS_HEADER_SIZE;
// evaluate and compare checksum of compressed data and header with written value
if (Block->Header.Compressed.Checksum != 0) {
ULONG Checksum;
Checksum = HbSimpleCheck (0, Buffer, AlignedCompressedSize + XPRESS_HEADER_SIZE);
if (((Checksum ^ Block->Header.Compressed.Checksum) & 0xffff) != 0) {
DBGOUT (("Compressed data checksum mismatch (got %08lx, written %08lx)\n", Checksum, Block->Header.Compressed.Checksum));
return(FALSE);
}
}
// Was this buffer compressed at all?
if (CompressedSize == UncompressedSize) {
// Nope, do not decompress it -- set bounds and return OK
Block->DataPtr = Buffer + XPRESS_HEADER_SIZE;
} else {
LONG DecodedSize;
// Decompress the buffer
DecodedSize = XpressDecode (NULL,
Block->DataPtr,
UncompressedSize,
UncompressedSize,
Buffer + XPRESS_HEADER_SIZE,
CompressedSize);
if (DecodedSize != UncompressedSize) {
DBGOUT (("Decode error: DecodedSize = %d, UncompressedSize = %d\n", DecodedSize, UncompressedSize));
return(FALSE);
}
}
#ifdef HIBER_DEBUG
// evaluate and compare uncompressed data checksums (just to be sure)
if (Block->Header.Uncompressed.Checksum != 0) {
ULONG Checksum;
Checksum = HbSimpleCheck (0, Block->DataPtr, UncompressedSize);
if (((Checksum ^ Block->Header.Uncompressed.Checksum) & 0xffff) != 0) {
DBGOUT (("Decoded data checksum mismatch (got %08lx, written %08lx)\n", Checksum, Block->Header.Uncompressed.Checksum));
return(FALSE);
}
}
#endif /* HIBER_DEBUG */
return(TRUE);
}
BOOLEAN
HbReadNextCompressedPageLZNT1 (
PUCHAR DestVa,
PCOMPRESSED_BUFFER CompressedBuffer
)
/*++
Routine Description:
This routine reads in the next compressed page from the
Hiber file and decompresses it into a designated region
of virtual memory.
The page is recreated by assembling it from a series
a compressed chunks that are assumed to be contiguously
stored in the Hiber file.
All reads from the Hiber file occurr at the file's
current offset forcing compressed pages to be read
in a continuous fashion without extraneous file seeks.
Arguments:
DestVa - The Virtual Address where the decompressed page should
be written.
CompressedBuffer - Descriptor of data already brought in
Return Value:
TRUE if the operation is successful, FALSE otherwise.
--*/
{
ULONG ReadTotal;
// Loop while page is incomplete
for (ReadTotal = 0; ReadTotal < PAGE_SIZE; ReadTotal += PO_COMPRESS_CHUNK_SIZE) {
// Get a chunk
if (!HbReadNextCompressedChunkLZNT1(DestVa, CompressedBuffer)) {
return FALSE;
}
// Move on to the next chunk of the page
DestVa += PO_COMPRESS_CHUNK_SIZE;
}
return TRUE;
}
BOOLEAN
HbReadNextCompressedChunkLZNT1 (
PUCHAR DestVa,
PCOMPRESSED_BUFFER CompressedBuffer
)
/*++
Routine Description:
Reads and decompresses the next compressed chunk from the Hiber file
and stores it in a designated region of virtual memory.
Since no master data structure exists within the Hiber file to identify
the location of all of the compression chunks, this routine operates
by reading sections of the Hiber file into a compression buffer
and extracting chunks from that buffer.
Chunks are extracted by determining if a chunk is completely present in the buffer
using the RtlDescribeChunk API. If the chunk is not completely present,
more of the Hiber file is read into the buffer until the chunk can
be extracted.
All reads from the Hiber file occurr at its current offset, forcing
compressed chunks to be read in a continous fashion with no extraneous
seeks.
Arguments:
DestVa - The virtual address where the decompressed chunk
should be written.
CompressedBuffer - Descriptor of data already brought in
Return Value:
TRUE if a chunk has been succesfully extracted and decompressed, FALSE otherwise.
--*/
{
PUCHAR Buffer;
NTSTATUS Status;
ULONG ChunkSize;
PUCHAR ChunkBuffer;
ULONG SpaceLeft;
// Loop until we have accomplished our goal since we may need
// several operations before a chunk is extracted
while (1) {
Buffer = CompressedBuffer->Current.Beg;
// Check the first unextracted chunk in the buffer
Status = RtlDescribeChunk(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_STANDARD,
&Buffer,
CompressedBuffer->Current.End,
&ChunkBuffer,
&ChunkSize);
switch (Status) {
case STATUS_SUCCESS:
// A complete and valid chunk is present in the buffer
// Decompress the chunk into the proper region of virtual memory
Status = RtlDecompressBuffer (COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_STANDARD,
DestVa,
PO_COMPRESS_CHUNK_SIZE,
CompressedBuffer->Current.Beg,
(LONG) (CompressedBuffer->Current.End - CompressedBuffer->Current.Beg),
&ChunkSize);
if ((!NT_SUCCESS(Status)) || (ChunkSize != PO_COMPRESS_CHUNK_SIZE)) {
// Decompression failed
return(FALSE);
} else {
// Decompression succeeded, indicate that the chunk following
// this one is the next unextracted chunk in the buffer
CompressedBuffer->Current.Beg = Buffer;
return(TRUE);
}
case STATUS_BAD_COMPRESSION_BUFFER:
case STATUS_NO_MORE_ENTRIES:
//
// Buffer does not contain a complete and valid chunk
//
//
// Check how much space remains in the buffer since
// we will need to read some stuff from the Hiber file
//
SpaceLeft = (LONG) (CompressedBuffer->Aligned.End - CompressedBuffer->Aligned.Beg);
if (SpaceLeft > LZNT1_COMPRESSION_BUFFER_SIZE) {
SpaceLeft = LZNT1_COMPRESSION_BUFFER_SIZE;
}
SpaceLeft -= (((LONG) (CompressedBuffer->Current.End - CompressedBuffer->Current.Beg)) + PAGE_MASK) & ~PAGE_MASK;
if (SpaceLeft <= 0) {
// Should never happen
DBGOUT (("SpaceLeft = %d\n", SpaceLeft));
return(FALSE);
}
if (!HbReadNextCompressedPages (SpaceLeft, CompressedBuffer)) {
// IO error
return(FALSE);
}
break;
default:
//
// Unhandled RtlDescribeChunk return code - have they changed the function on us?
//
return(FALSE);
}
//
// try again with the bigger buffer
//
}
return FALSE;
}
VOID
HexDump (
IN ULONG indent,
IN ULONG va,
IN ULONG len,
IN ULONG width,
IN PUCHAR buf
)
{
TCHAR s[80], t[80], lstr[200];
PTCHAR ps, pt;
ULONG i;
UCHAR Key;
static UCHAR rgHexDigit[] = "0123456789abcdef";
UNREFERENCED_PARAMETER( width );
if (HiberIoError) {
HbPrint (TEXT("*** HiberIoError\n"));
return ;
}
if (HiberOutOfRemap) {
HbPrint (TEXT("*** HiberOutOfRemap\n"));
return ;
}
i = 0;
while (len) {
ps = s;
pt = t;
ps[0] = TEXT('\0');
pt[0] = TEXT('*');
pt++;
for (i=0; i < 16; i++) {
ps[0] = TEXT(' ');
ps[1] = TEXT(' ');
ps[2] = TEXT(' ');
if (len) {
ps[0] = rgHexDigit[buf[0] >> 4];
ps[1] = rgHexDigit[buf[0] & 0xf];
pt[0] = ((TCHAR)buf[0] < TEXT(' ')) || ((TCHAR)buf[0] > TEXT('z')) ? TEXT('.') : buf[0];
len -= 1;
buf += 1;
pt += 1;
}
ps += 3;
}
ps[0] = 0;
pt[0] = TEXT('*');
pt[1] = 0;
s[23] = TEXT('-');
if (s[0]) {
_stprintf (lstr, TEXT("%*s%08lx: %s %s\r\n"), indent, TEXT(""), va, s, t);
HbPrint (lstr);
va += 16;
}
}
ArcRead(ARC_CONSOLE_INPUT, &Key, sizeof(Key), &i);
}
BOOLEAN
HbReadDelayedBlock (
BOOLEAN ForceDecoding,
PFN_NUMBER DestPage,
ULONG RangeCheck,
PDECOMPRESSED_BLOCK Block,
PCOMPRESSED_BUFFER CompressedBuffer
)
{
LONG i, j;
BOOLEAN Contig;
BOOLEAN Ret;
if (ForceDecoding) {
if (Block->DelayedCnt == 0) {
return TRUE;
}
} else {
// If first page to delay read next block info
if (Block->DelayedCnt <= 0) {
Ret = HbReadNextCompressedBlockHeader (Block, CompressedBuffer);
if (HiberIoError || !Ret || !Block->Header.XpressEncoded) {
// Something is wrong
return FALSE;
}
}
// remember page info
Block->Delayed[Block->DelayedCnt].DestPage = DestPage;
Block->Delayed[Block->DelayedCnt].RangeCheck = RangeCheck;
// Update counter
Block->DelayedCnt += 1;
// Last page that may be delayed?
if (Block->DelayedCnt != sizeof (Block->Delayed) / sizeof (Block->Delayed[0]) &&
(Block->DelayedCnt << PAGE_SHIFT) < Block->Header.Uncompressed.Size) {
// Nope, nothing to do
return TRUE;
}
}
// Make sure that size of encoded block and # of delayed pages are the same
if ((Block->DelayedCnt << PAGE_SHIFT) != Block->Header.Uncompressed.Size) {
DBGOUT (("DelayedCnt = %d, UncompressedSize = %d\n", Block->DelayedCnt, Block->Header.Uncompressed.Size));
return FALSE;
}
// Prepare for mapping. Hopefully mapping will be contiguous
Contig = TRUE;
// Map new pages
for (j = 0; j < Block->DelayedCnt; ++j) {
i = HbPageDisposition (Block->Delayed[j].DestPage);
if (i == HbPageInvalid) {
// Should never happen
return(FALSE);
}
if (i == HbPageNotInUse) {
Block->Delayed[j].DestVa = HbMapPte(PTE_XPRESS_DEST_FIRST + j, Block->Delayed[j].DestPage);
} else {
Block->Delayed[j].DestVa = HbNextSharedPage(PTE_XPRESS_DEST_FIRST + j, Block->Delayed[j].DestPage);
}
if (j > 0 && Block->Delayed[j].DestVa != Block->Delayed[j-1].DestVa + PAGE_SIZE) {
Contig = FALSE;
}
}
// Set pointer to data. Try mapped pages if possible
if (Contig) {
Block->DataSize = Block->DelayedCnt << PAGE_SHIFT;
Block->DataPtr = Block->Delayed[0].DestVa;
} else {
// Will have to used preallocated data buffer
Block->DataSize = Block->Header.Uncompressed.Size;
Block->DataPtr = Block->PreallocatedDataBuffer;
}
// Decode next block
Ret = HbReadNextCompressedBlock (Block, CompressedBuffer);
// Check for errors
if (HiberIoError || !Ret) {
// Something's seriousely wrong
return FALSE;
}
for (j = 0; j < Block->DelayedCnt; ++j) {
// Copy block to target address if necessary
if (Block->Delayed[j].DestVa != Block->DataPtr) {
RtlCopyMemory (Block->Delayed[j].DestVa, Block->DataPtr, PAGE_SIZE);
}
Block->DataPtr += PAGE_SIZE;
Block->DataSize -= PAGE_SIZE;
}
// No more delayed blocks
Block->DelayedCnt = 0;
return TRUE;
}
// Allocate data aligned on page boundary
PVOID
HbAllocateAlignedHeap (
ULONG Size
)
{
PCHAR Va;
Va = BlAllocateHeap (Size + PAGE_MASK);
if (Va != NULL) {
Va += ((PAGE_SIZE - (((ULONG_PTR) Va) & PAGE_MASK)) & PAGE_MASK);
}
return (Va);
}
//
// structure used to get typecast to
// function pointer from data pointer
// to compile w4
// (WakeDispatch)
//
typedef struct {
PHIBER_WAKE_DISPATCH Dispatch;
} _WAKE_DISPATCH, * _PWAKE_DISPATCH;
ULONG
HbRestoreFile (
IN PULONG Information,
OUT PCHAR *BadLinkName
)
{
PPO_MEMORY_IMAGE MemImage;
PPO_IMAGE_LINK ImageLink;
PPO_MEMORY_RANGE_ARRAY Table;
PHIBER_WAKE_DISPATCH WakeDispatch = 0;
ULONG Length;
ULONG Check, CheckSum;
PUCHAR p1;
PUCHAR DestVa;
ULONG Index, i;
PFN_NUMBER TablePage;
PFN_NUMBER DestPage;
PFN_NUMBER Scale;
ULONG TotalPages;
ULONG LastBar;
ULONG Sel[4];
ULONG LinkedDrive;
COMPRESSED_BUFFER CompressedBufferData;
PCOMPRESSED_BUFFER CompressedBuffer = &CompressedBufferData;
BOOLEAN Ret;
LONG XpressEncoded;
PDECOMPRESSED_BLOCK Block;
ULONG fPercentage = 0;
ULONG LastPercentage = (ULONG)-1;
PUCHAR Ptr;
ARC_STATUS Status;
ULONG ActualBase;
FILE_INFORMATION FileInfo;
#if HIBER_PERF_STATS
ULONG StartTime, EndTime;
StartTime = ArcGetRelativeTime();
#endif
#if defined (HIBER_DEBUG)
HbPrint(TEXT("HbRestoreFile\r\n"));
#endif
*Information = 0;
HiberBufferPage = 0;
BlAllocateAlignedDescriptor (LoaderFirmwareTemporary,
0,
1,
1,
&HiberBufferPage);
CHECK_ERROR (!HiberBufferPage, HIBER_ERROR_NO_MEMORY);
HiberBuffer = (PUCHAR) (KSEG0_BASE | (((ULONG)HiberBufferPage) << PAGE_SHIFT));
//
// Read image header
//
HbReadPage (PO_IMAGE_HEADER_PAGE, HiberBuffer);
MemImage = (PPO_MEMORY_IMAGE) HiberBuffer;
//
// If the signature is a link, then follow it
//
if (MemImage->Signature == PO_IMAGE_SIGNATURE_LINK) {
ImageLink = (PPO_IMAGE_LINK) HiberBuffer;
//
// Open target partition, and then the hiberfile image on that
// partition. If not found, then we're done
//
Status = ArcOpen ((char*)ImageLink->Name, ArcOpenReadOnly, &LinkedDrive);
if (Status != ESUCCESS) {
if (ARGUMENT_PRESENT(BadLinkName)) {
*BadLinkName = (char *)(&ImageLink->Name);
//
// At this point we want to blast the link signature. The caller
// may need to load NTBOOTDD to access the real hiberfile. Once
// this happens there is no turning back as we cannot go back to
// the BIOS to reread BOOT.INI. By zeroing the signature we ensure
// that if the restore fails, the next boot will not try to restore
// it again.
//
HbSetImageSignature(0);
}
return 0;
}
Status = BlOpen (LinkedDrive, (PCHAR)szHiberFileName, ArcOpenReadWrite, &i);
if (Status != ESUCCESS) {
ArcClose(LinkedDrive);
return 0;
}
//
// Switch to linked HiberFile image and continue
//
BlClose (HiberFile);
HiberFile = i;
HbReadPage (PO_IMAGE_HEADER_PAGE, HiberBuffer);
}
//
// If the image has the wake signature, then we've already attempted
// to restart this image once. Check if it should be attempted again
//
if (MemImage->Signature == PO_IMAGE_SIGNATURE_WAKE) {
Sel[0] = HIBER_CANCEL;
Sel[1] = HIBER_CONTINUE;
Sel[2] = HIBER_DEBUG_BREAK_ON_WAKE;
Sel[3] = 0;
HbScreen(HIBER_RESTART_AGAIN);
i = HbSelection(PAUSE_X, PAUSE_Y, Sel, 1);
if (i == 0) {
HiberAbort = TRUE;
HbSetImageSignature (0);
return 0;
}
MemImage->Signature = PO_IMAGE_SIGNATURE;
}
//
// If the signature is not valid, then behave as if there's no
// hibernated context
//
if (MemImage->Signature != PO_IMAGE_SIGNATURE) {
return 0;
}
#if defined(_X86_)
//
// If hiber image is for Amd64, the following call will set
// BlAmd64UseLongMode to TRUE.
//
BlCheckForAmd64Image(MemImage);
#endif
CHECK_ERROR (READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, LengthSelf) > PAGE_SIZE,
HIBER_ERROR_BAD_IMAGE);
//
// Copy the image out of the HiberBuffer
//
Length = READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, LengthSelf);
MemImage = BlAllocateHeap(Length);
CHECK_ERROR (!MemImage, HIBER_ERROR_NO_MEMORY);
memcpy (MemImage, HiberBuffer, Length);
HiberImageFeatureFlags = READ_FIELD_ULONG(PO_MEMORY_IMAGE,
MemImage,
FeatureFlags);
//
// Verify the checksum on the image header
//
Check = READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, CheckSum);
WRITE_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, CheckSum, 0);
Check = Check - HbSimpleCheck(0, MemImage, Length);
CHECK_ERROR(Check, HIBER_ERROR_BAD_IMAGE);
CHECK_ERROR(READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, Version) != 0,
HIBER_IMAGE_INCOMPATIBLE);
CHECK_ERROR(READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, PageSize) != PAGE_SIZE,
HIBER_IMAGE_INCOMPATIBLE);
//
// Check to make sure the hiberfil matches with the
// amount of memory we think we have. We want to guard
// against folks who hibernate, then add/remove memory,
// then try to resume.
//
Status = BlGetFileInformation( HiberFile, &FileInfo );
if( Status == ESUCCESS ) {
ULONG FileSize;
ULONG MemorySize;
//
// Get the size of the file (in pages).
//
FileSize = (ULONG)(FileInfo.EndingAddress.QuadPart >> PAGE_SHIFT);
//
// Get the size of memory (in pages).
//
MemorySize = BlDetermineOSVisibleMemory();
//
// See if the file size matches the amount of memory we've got
// in the machine. Allow for 32MB of slop for hidden memory.
//
if( abs(FileSize - MemorySize) > (_24MB) ) {
#if 0
BlPrint( "Original FileSize: %d pages\n\r", FileSize );
BlPrint( "Original MemorySize: %d pages\n\r", MemorySize );
BlPrint( "Press any key to continue\n\r" );
while( !BlGetKey() );
#endif
//
// Put up an error message telling the user
// the memory configuration doesn't match
// the hiber file. If we return from telling
// the user, then mark the hiber file as
// invalid so we won't try it again, then
// proceed.
//
HbScreen(HIBER_ERROR);
HbPrintMsg(HIBER_MEMORY_INCOMPATIBLE);
Sel[0] = HIBER_CANCEL;
Sel[1] = 0;
HbSelection (FAULT_X, FAULT_Y+2, Sel, 0);
HiberAbort = TRUE;
HbSetImageSignature (0);
return 0;
}
}
//
// Setup mapping information for restore
//
#if !defined (_ALPHA_) && !defined(_IA64_)
HiberNoHiberPtes = READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, NoHiberPtes);
CHECK_ERROR (HiberNoHiberPtes > HIBER_PTES, HIBER_IMAGE_INCOMPATIBLE);
#endif
HiberNoMappings = READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, NoFreePages);
#if defined (_ALPHA_) || defined(_IA64_)
HiberImagePageSelf = MemImage->PageSelf; // used in WakeDispatch to enable break-on-wake
#else
if(BlAmd64UseLongMode) {
HiberIdentityVaAmd64 = READ_FIELD_ULONG64 (PO_MEMORY_IMAGE,
MemImage,
HiberVa);
} else {
HiberIdentityVa = (PVOID) MemImage->HiberVa;
}
HiberImagePageSelf = READ_FIELD_PFN_NUMBER (PO_MEMORY_IMAGE,
MemImage,
PageSelf);
//
// Allocate a block of PTEs for restoration work which
// do not overlap the same addresses needed for the
// restoration
//
//
// p1 is always initialized to NULL here as HiberVa is NULL at this point.
//
p1 = (HiberVa) ? HiberVa + (HIBER_PTES << PAGE_SHIFT) : 0;
if(BlAmd64UseLongMode) {
while (!HiberVa || (HiberIdentityVaAmd64 >= (ULONG64) HiberVa && HiberIdentityVaAmd64 <= (ULONG64) p1)) {
HbAllocatePtes (HIBER_PTES, &HiberPtes, &HiberVa);
p1 = HiberVa + (HIBER_PTES << PAGE_SHIFT);
}
} else {
while (!HiberVa || (MemImage->HiberVa >= (ULONG_PTR) HiberVa && MemImage->HiberVa <= (ULONG_PTR) p1)) {
HbAllocatePtes (HIBER_PTES, &HiberPtes, &HiberVa);
p1 = HiberVa + (HIBER_PTES << PAGE_SHIFT);
}
}
#endif
//
// Read in the free page map
//
HbReadPage (PO_FREE_MAP_PAGE, HiberBuffer);
Check = HbSimpleCheck(0, HiberBuffer, PAGE_SIZE);
CHECK_ERROR (READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, FreeMapCheck) != Check,
HIBER_ERROR_BAD_IMAGE);
// Set us up to decompress the contents of the hiber file
// Allocate a buffer for compression work
//
// N.B. The compression buffer size must be at least the maximum
// compressed size of a single compression chunk.
//
// Initialize decompressed data buffer
Ptr = HbAllocateAlignedHeap (sizeof (*Block) + XPRESS_MAX_SIZE);
CHECK_ERROR(!Ptr, HIBER_ERROR_NO_MEMORY);
Block = (PVOID) (Ptr + XPRESS_MAX_SIZE);
Block->DataSize = 0;
Block->PreallocatedDataBuffer = Ptr;
//
// Allocate compressed data buffer. Change the allocation policy
// to lowest first in order to get a buffer under 1MB. This saves
// us from double-buffering all the BIOS transfers.
//
Status = BlAllocateAlignedDescriptor(LoaderFirmwareTemporary,
0,
MAX_COMPRESSION_BUFFER_PAGES + MAX_COMPRESSION_BUFFER_EXTRA_PAGES,
0x10000 >> PAGE_SHIFT,
&ActualBase);
if (Status == ESUCCESS) {
Ptr = (PVOID)(KSEG0_BASE | (ActualBase << PAGE_SHIFT));
} else {
Ptr = HbAllocateAlignedHeap (MAX_COMPRESSION_BUFFER_SIZE + MAX_COMPRESSION_BUFFER_EXTRA_SIZE);
}
CHECK_ERROR(!Ptr, HIBER_ERROR_NO_MEMORY);
// Initialize compressed data buffer
CompressedBuffer->Buffer.Beg = Ptr;
CompressedBuffer->Buffer.End = Ptr + MAX_COMPRESSION_BUFFER_SIZE + MAX_COMPRESSION_BUFFER_EXTRA_SIZE;
CompressedBuffer->Aligned.Beg = CompressedBuffer->Buffer.Beg;
CompressedBuffer->Aligned.End = CompressedBuffer->Buffer.End;
CompressedBuffer->FilePage = 0;
CompressedBuffer->NeedSeek = TRUE;
CompressedBuffer->Current.Beg = CompressedBuffer->Current.End = CompressedBuffer->Aligned.Beg;
// ***************************************************************
//
// From here on, there's no memory allocation from the loaders
// heap. This is to simplify the booking of whom owns which
// page. If the hibernation process is aborted, then the
// pages used here are simply forgoten and the loader continues.
// If the hiberation processor completes, we forget about
// the pages in use by the loader
//
// ***************************************************************
#if defined(_ALPHA_) || defined(_IA64_)
//
// Initialize the hibernation memory allocation and remap table,
// using the free page map just read from the hibernation file.
//
HbInitRemap((PPFN_NUMBER) HiberBuffer); // why can't HiberBuffer be a PVOID?
#else // original (x86) code
//
// Set the loader map pointer to the tempory buffer, and get
// a physical shared page to copy the map to.
//
HbMapPte(PTE_MAP_PAGE, HiberBufferPage);
HbMapPte(PTE_REMAP_PAGE, HiberBufferPage);
DestVa = HbNextSharedPage(PTE_MAP_PAGE, 0);
memcpy (DestVa, HiberBuffer, PAGE_SIZE);
DestVa = HbNextSharedPage(PTE_REMAP_PAGE, 0);
#endif // Alpha/x86
//
// Map in and copy relocatable hiber wake dispatcher
//
Length = (ULONG) (&WakeDispatcherEnd - &WakeDispatcherStart);
p1 = (PUCHAR) &WakeDispatcherStart;
#if defined(_X86_)
if(BlAmd64UseLongMode) {
Length = (ULONG) (&WakeDispatcherAmd64End - &WakeDispatcherAmd64Start);
p1 = (PUCHAR) &WakeDispatcherAmd64Start;
}
#endif
Index = 0;
while (Length) {
CHECK_ERROR(PTE_DISPATCHER_START+Index > PTE_DISPATCHER_END, HIBER_INTERNAL_ERROR);
DestVa = HbNextSharedPage(PTE_DISPATCHER_START+Index, 0);
if (Index == 0) {
WakeDispatch = ((_PWAKE_DISPATCH) &DestVa)->Dispatch;
}
i = Length > PAGE_SIZE ? PAGE_SIZE : Length;
memcpy (DestVa, p1, i);
Length -= i;
p1 += i;
Index += 1;
}
//
// Read the hibernated processors context
//
// Note we read into the hiber buffer and then copy in order to
// ensure that the destination of the I/O is legal to transfer into.
// Busmaster ISA SCSI cards can only access the low 16MB of RAM.
//
DestVa = HbNextSharedPage(PTE_HIBER_CONTEXT, 0);
HbReadPage (PO_PROCESSOR_CONTEXT_PAGE, HiberBuffer);
memcpy(DestVa, HiberBuffer, PAGE_SIZE);
Check = HbSimpleCheck(0, DestVa, PAGE_SIZE);
CHECK_ERROR(READ_FIELD_ULONG(PO_MEMORY_IMAGE, MemImage, WakeCheck) != Check,
HIBER_ERROR_BAD_IMAGE);
#if defined(_ALPHA_)
HiberWakeState = (PKPROCESSOR_STATE)DestVa;
#endif
#if defined(_X86_)
//
// Check if OS was in PAE mode
//
if (!BlAmd64UseLongMode &&
((PKPROCESSOR_STATE)(DestVa))->SpecialRegisters.Cr4 & CR4_PAE) {
BlUsePae = TRUE;
}
#endif
//
// Perform architecture specific setup for dispatcher, then set
// the location of first remap past the pages mapped so far
//
HiberSetupForWakeDispatch ();
HiberFirstRemap = HiberLastRemap;
//
// Restore memory from hibernation image
//
TablePage = READ_FIELD_PFN_NUMBER(PO_MEMORY_IMAGE, MemImage, FirstTablePage);
Table = (PPO_MEMORY_RANGE_ARRAY) HiberBuffer;
Scale = READ_FIELD_PFN_NUMBER(PO_MEMORY_IMAGE, MemImage, TotalPages) /
PERCENT_BAR_WIDTH;
LastBar = 0;
TotalPages = 3;
//
// Popup "Resuming Windows 2000..." message
//
BlSetProgBarCharacteristics(HIBER_UI_BAR_ELEMENT, BLDR_UI_BAR_BACKGROUND);
BlOutputStartupMsg(BL_MSG_RESUMING_WINDOWS);
BlOutputTrailerMsg(BL_ADVANCED_BOOT_MESSAGE);
XpressEncoded = -1; // unknown encoding (either Xpress or LZNT1)
Block->DataSize = 0; // no data left in buffer
Block->DelayedCnt = 0; // no delayed blocks
Block->DelayedChecksum = 0; // delayed checksum = 0;
Block->DelayedBadChecksum = FALSE;
while (TablePage) {
#if defined (HIBER_DEBUG) && (HIBER_DEBUG & 2)
SHOWNUM(TablePage);
#endif
//
// Do not use HbReadPage if possible -- it issues extra seek
// (usually 5-6 ms penalty) -- use sequential read if possible
//
if (CompressedBuffer->FilePage == 0 ||
TablePage > CompressedBuffer->FilePage ||
TablePage < CompressedBuffer->FilePage - (PFN_NUMBER) ((CompressedBuffer->Current.End - CompressedBuffer->Current.Beg) >> PAGE_SHIFT)) {
//
// Cannot read table page from current buffer -- need to seek
// and reset the buffer (should happen on very first entry only)
//
CompressedBuffer->FilePage = TablePage;
CompressedBuffer->Current.Beg = CompressedBuffer->Current.End = CompressedBuffer->Aligned.Beg;
CompressedBuffer->NeedSeek = TRUE;
}
//
// Shift current pointer to the page we need
//
CompressedBuffer->Current.Beg = CompressedBuffer->Current.End - ((CompressedBuffer->FilePage - TablePage) << PAGE_SHIFT);
//
// Make sure the page is in
//
Ret = HbReadNextCompressedPages (PAGE_SIZE, CompressedBuffer);
CHECK_ERROR(HiberIoError, HIBER_READ_ERROR);
CHECK_ERROR(!Ret, HIBER_ERROR_BAD_IMAGE);
//
// Copy table page to target location and adjust input pointer
//
RtlCopyMemory (Table, CompressedBuffer->Current.Beg, PAGE_SIZE);
CompressedBuffer->Current.Beg += PAGE_SIZE;
Check = READ_FIELD_ULONG(PO_MEMORY_RANGE_ARRAY_LINK,
Table,
CheckSum);
if (Check) {
WRITE_FIELD_ULONG(PO_MEMORY_RANGE_ARRAY_LINK,
Table,
CheckSum,
0);
Check = Check - HbSimpleCheck(0, Table, PAGE_SIZE);
CHECK_ERROR(Check, HIBER_ERROR_BAD_IMAGE);
}
// Check the first block magic to see whether it LZNT1 or Xpress
if (XpressEncoded < 0) {
Ret = HbReadNextCompressedBlockHeader (Block, CompressedBuffer);
CHECK_ERROR(HiberIoError, HIBER_READ_ERROR);
CHECK_ERROR(!Ret, HIBER_ERROR_BAD_IMAGE);
// Remember the mode
XpressEncoded = (BOOLEAN) (Block->Header.XpressEncoded);
}
for(Index=1;
Index <= READ_FIELD_ULONG(PO_MEMORY_RANGE_ARRAY_LINK, Table, EntryCount);
Index++) {
Check = 0;
DestPage = READ_ELEMENT_FIELD_PFN_NUMBER(
PO_MEMORY_RANGE_ARRAY_RANGE,
Table,
Index,
StartPage
);
while (DestPage < READ_ELEMENT_FIELD_PFN_NUMBER(
PO_MEMORY_RANGE_ARRAY_RANGE,
Table,
Index,
EndPage)) {
if (!XpressEncoded) {
// LZNT1 encoding -- do one page at a time
//
// If this page conflicts with something in the
// loader, then use the next mapping
//
i = HbPageDisposition (DestPage);
CHECK_ERROR(i == HbPageInvalid, HIBER_ERROR_BAD_IMAGE);
if (i == HbPageNotInUse) {
DestVa = HbMapPte(PTE_DEST, DestPage);
} else {
DestVa = HbNextSharedPage(PTE_DEST, DestPage);
}
Ret = HbReadNextCompressedPageLZNT1 (DestVa, CompressedBuffer);
CHECK_ERROR(HiberIoError, HIBER_READ_ERROR);
CHECK_ERROR(!Ret, HIBER_ERROR_BAD_IMAGE);
Check = HbSimpleCheck(Check, DestVa, PAGE_SIZE);
} else {
CheckSum = READ_ELEMENT_FIELD_ULONG(
PO_MEMORY_RANGE_ARRAY_RANGE,
Table,
Index,
CheckSum
);
Ret = HbReadDelayedBlock (FALSE,
DestPage,
CheckSum,
Block,
CompressedBuffer);
CHECK_ERROR(HiberIoError, HIBER_READ_ERROR);
CHECK_ERROR(!Ret, HIBER_ERROR_BAD_IMAGE);
}
// Update counters
DestPage += 1;
TotalPages += 1;
fPercentage = (ULONG)((TotalPages * 100) /
READ_FIELD_PFN_NUMBER (PO_MEMORY_IMAGE, MemImage, TotalPages));
if (fPercentage != LastPercentage) {
BlUpdateProgressBar(fPercentage);
HbCheckForPause();
LastPercentage = fPercentage;
}
}
CHECK_ERROR(HiberOutOfRemap, HIBER_ERROR_OUT_OF_REMAP);
//
// Verify checksum on range, but allow continuation with debug flag
//
CheckSum = READ_ELEMENT_FIELD_ULONG (PO_MEMORY_RANGE_ARRAY_RANGE,
Table,
Index,
CheckSum);
if (!XpressEncoded && Check != CheckSum) {
Block->DelayedBadChecksum = TRUE;
}
if (Block->DelayedBadChecksum && !HiberBreakOnWake) {
ChecksumError:
Block->DelayedBadChecksum = FALSE;
#if defined (HIBER_DEBUG) && (HIBER_DEBUG & 2)
{
TCHAR lstr[80];
HbPrint (TEXT("\r\n"));
_stprintf (lstr,
TEXT("TP:%x IDX:%x FP:%x SP:%x EP:%x CHK:%x-%x\r\n"),
TablePage,
Index,
READ_ELEMENT_FIELD_PFN_NUMBER(PO_MEMORY_RANGE_ARRAY_RANGE, Table, Index, PageNo),
READ_ELEMENT_FIELD_PFN_NUMBER(PO_MEMORY_RANGE_ARRAY_RANGE, Table, Index, StartPage),
READ_ELEMENT_FIELD_PFN_NUMBER(PO_MEMORY_RANGE_ARRAY_RANGE, Table, Index, EndPage),
READ_ELEMENT_FIELD_ULONG(PO_MEMORY_RANGE_ARRAY_RANGE, Table, Index, CheckSum),
Check );
HbPrint(lstr);
HexDump (2, (DestPage-1) << PAGE_SHIFT, 0x100, 4, DestVa);
}
#endif
#ifdef HIBER_DEBUG
DBGOUT ((TEXT("Checksum error\n")));
HbPause ();
#endif
HbScreen(HIBER_ERROR);
HbPrintMsg(HIBER_ERROR_BAD_IMAGE);
Sel[0] = HIBER_CANCEL;
Sel[1] = HIBER_DEBUG_BREAK_ON_WAKE;
Sel[2] = 0;
i = HbSelection (FAULT_X, FAULT_Y, Sel, 1);
if (i == 0) {
HiberAbort = TRUE;
HbSetImageSignature (0);
return 0;
}
}
}
TablePage = READ_FIELD_PFN_NUMBER (PO_MEMORY_RANGE_ARRAY_LINK,
Table,
NextTable);
}
// Process the rest of delayed pages if necessary
if (XpressEncoded > 0) {
Ret = HbReadDelayedBlock (TRUE,
0,
0,
Block,
CompressedBuffer);
CHECK_ERROR(HiberIoError, HIBER_READ_ERROR);
CHECK_ERROR(!Ret, HIBER_ERROR_BAD_IMAGE);
if (Block->DelayedBadChecksum) {
goto ChecksumError;
}
}
//
// Set the image signature to wake
//
HbSetImageSignature (PO_IMAGE_SIGNATURE_WAKE);
#if HIBER_PERF_STATS
EndTime = ArcGetRelativeTime();
BlPositionCursor(BAR_X, BAR_Y + 5);
HbPrint(TEXT("HIBER: Restore File took "));
HbPrintNum(EndTime - StartTime);
HbPrint(TEXT("\r\n"));
HbPause();
#endif
//
// Check hiber flags to see if it is necessary to reconnect APM
// or enable no-execute feature
//
if (READ_FIELD_UCHAR(PO_MEMORY_IMAGE, MemImage, HiberFlags) &
PO_HIBER_NO_EXECUTE) {
HiberNoExecute = TRUE;
}
if (READ_FIELD_UCHAR(PO_MEMORY_IMAGE, MemImage, HiberFlags) &
PO_HIBER_APM_RECONNECT) {
//
// attempt apm restart
//
DoApmAttemptReconnect();
}
//
// Use architecture specific relocatable code to perform the final wake dispatcher
//
if (WakeDispatch) {
WakeDispatch();
}
CHECK_ERROR (TRUE, HIBER_INTERNAL_ERROR);
}
VOID
HbSetImageSignature (
IN ULONG NewSignature
)
{
LARGE_INTEGER li;
ULONG Count, Status;
li.QuadPart = 0;
Status = BlSeek (HiberFile, &li, SeekAbsolute);
if (Status == ESUCCESS) {
BlWrite (HiberFile, &NewSignature, sizeof(ULONG), &Count);
}
}