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
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);
|
|
}
|
|
}
|
|
|