6703 lines
177 KiB
C
6703 lines
177 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
dumpctl.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code to dump memory to disk after a crash.
|
||
|
||
Author:
|
||
|
||
Darryl E. Havens (darrylh) 17-dec-1993
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "iomgr.h"
|
||
#include "dumpctl.h"
|
||
#include "ntddft.h"
|
||
#include <inbv.h>
|
||
#include <windef.h>
|
||
#define NOEXTAPI
|
||
#include <wdbgexts.h>
|
||
|
||
extern KDDEBUGGER_DATA64 KdDebuggerDataBlock;
|
||
|
||
typedef struct _TRIAGE_PTR_DATA_BLOCK {
|
||
PUCHAR MinAddress;
|
||
PUCHAR MaxAddress;
|
||
} TRIAGE_PTR_DATA_BLOCK, *PTRIAGE_PTR_DATA_BLOCK;
|
||
|
||
// A triage dump is sixteen pages long. Some of that is
|
||
// header information and at least a few other pages will
|
||
// be used for basic dump information so limit the number
|
||
// of extra data blocks to something less than sixteen
|
||
// to save array space.
|
||
#define IO_MAX_TRIAGE_DUMP_DATA_BLOCKS 8
|
||
|
||
//
|
||
// Global variables
|
||
//
|
||
|
||
extern PVOID MmPfnDatabase;
|
||
extern PFN_NUMBER MmHighestPossiblePhysicalPage;
|
||
|
||
#if defined (_IA64_)
|
||
extern PFN_NUMBER MmSystemParentTablePage;
|
||
#endif
|
||
|
||
ULONG IopAutoReboot;
|
||
|
||
NTSTATUS IopFinalCrashDumpStatus = -1;
|
||
ERESOURCE IopCrashDumpLock;
|
||
|
||
ULONG IopNumTriageDumpDataBlocks;
|
||
TRIAGE_PTR_DATA_BLOCK IopTriageDumpDataBlocks[IO_MAX_TRIAGE_DUMP_DATA_BLOCKS];
|
||
|
||
//
|
||
// If space is available in a triage dump it's possible
|
||
// to add "interesting" data pages referenced by runtime
|
||
// information such as context registers. The following
|
||
// lists are offsets into the CONTEXT structure of pointers
|
||
// which usually point to interesting data. They are
|
||
// in priority order.
|
||
//
|
||
|
||
#define IOP_LAST_CONTEXT_OFFSET 0xffff
|
||
|
||
#if defined(_X86_)
|
||
USHORT IopRunTimeContextOffsets[] = {
|
||
FIELD_OFFSET(CONTEXT, Ebx),
|
||
FIELD_OFFSET(CONTEXT, Esi),
|
||
FIELD_OFFSET(CONTEXT, Edi),
|
||
FIELD_OFFSET(CONTEXT, Ecx),
|
||
FIELD_OFFSET(CONTEXT, Edx),
|
||
FIELD_OFFSET(CONTEXT, Eax),
|
||
FIELD_OFFSET(CONTEXT, Eip),
|
||
IOP_LAST_CONTEXT_OFFSET
|
||
};
|
||
#elif defined(_IA64_)
|
||
USHORT IopRunTimeContextOffsets[] = {
|
||
FIELD_OFFSET(CONTEXT, IntS0),
|
||
FIELD_OFFSET(CONTEXT, IntS1),
|
||
FIELD_OFFSET(CONTEXT, IntS2),
|
||
FIELD_OFFSET(CONTEXT, IntS3),
|
||
FIELD_OFFSET(CONTEXT, StIIP),
|
||
IOP_LAST_CONTEXT_OFFSET
|
||
};
|
||
#elif defined(_AMD64_)
|
||
USHORT IopRunTimeContextOffsets[] = {
|
||
FIELD_OFFSET(CONTEXT, Rbx),
|
||
FIELD_OFFSET(CONTEXT, Rsi),
|
||
FIELD_OFFSET(CONTEXT, Rdi),
|
||
FIELD_OFFSET(CONTEXT, Rcx),
|
||
FIELD_OFFSET(CONTEXT, Rdx),
|
||
FIELD_OFFSET(CONTEXT, Rax),
|
||
FIELD_OFFSET(CONTEXT, Rip),
|
||
IOP_LAST_CONTEXT_OFFSET
|
||
};
|
||
#else
|
||
USHORT IopRunTimeContextOffsets[] = {
|
||
IOP_LAST_CONTEXT_OFFSET
|
||
};
|
||
#endif
|
||
|
||
//
|
||
// Set IopIgnoreDumpCheck to TRUE when debugging dumps to prevent
|
||
// the checksum from interfering with debugging.
|
||
//
|
||
|
||
LOGICAL IopIgnoreDumpCheck = FALSE;
|
||
|
||
//
|
||
// Max dump transfer sizes
|
||
//
|
||
|
||
#define IO_DUMP_MAXIMUM_TRANSFER_SIZE ( 1024 * 64 )
|
||
#define IO_DUMP_MINIMUM_TRANSFER_SIZE ( 1024 * 32 )
|
||
#define IO_DUMP_MINIMUM_FILE_SIZE ( PAGE_SIZE * 256 )
|
||
#define MAX_UNICODE_LENGTH ( 512 )
|
||
|
||
#define DEFAULT_DRIVER_PATH ( L"\\SystemRoot\\System32\\Drivers\\" )
|
||
#define DEFAULT_DUMP_DRIVER ( L"\\SystemRoot\\System32\\Drivers\\diskdump.sys" )
|
||
#define SCSIPORT_DRIVER_NAME ( L"scsiport.sys" )
|
||
#define STORPORT_DRIVER_NAME ( L"storport.sys" )
|
||
#ifdef _WIN64
|
||
#define MAX_TRIAGE_STACK_SIZE ( 32 * 1024 )
|
||
#else
|
||
#define MAX_TRIAGE_STACK_SIZE ( 16 * 1024 )
|
||
#endif
|
||
#define DEFAULT_TRIAGE_DUMP_FLAGS ( 0xFFFFFFFF )
|
||
|
||
//
|
||
// for memory allocations
|
||
//
|
||
|
||
#define DUMP_TAG ('pmuD')
|
||
#undef ExAllocatePool
|
||
#define ExAllocatePool(Pool,Size) ExAllocatePoolWithTag(Pool,Size,DUMP_TAG)
|
||
|
||
//
|
||
// Function prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
IoConfigureCrashDump(
|
||
CRASHDUMP_CONFIGURATION Configuration
|
||
);
|
||
|
||
BOOLEAN
|
||
IoInitializeCrashDump(
|
||
IN HANDLE hPageFile
|
||
);
|
||
|
||
NTSTATUS
|
||
IopWriteTriageDump(
|
||
IN ULONG FieldsToWrite,
|
||
IN PDUMP_DRIVER_WRITE WriteRoutine,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG DiverTransferSize,
|
||
IN PCONTEXT Context,
|
||
IN PKTHREAD Thread,
|
||
IN LPBYTE Buffer,
|
||
IN ULONG BufferSize,
|
||
IN ULONG ServicePackBuild,
|
||
IN ULONG TriageOptions
|
||
);
|
||
|
||
NTSTATUS
|
||
IopWriteSummaryDump(
|
||
IN PRTL_BITMAP PageMap,
|
||
IN PDUMP_DRIVER_WRITE WriteRoutine,
|
||
IN PANSI_STRING ProgressMessage,
|
||
IN PUCHAR MessageBuffer,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN ULONG DiverTransferSize
|
||
);
|
||
|
||
NTSTATUS
|
||
IopWriteToDisk(
|
||
IN PVOID Buffer,
|
||
IN ULONG WriteLength,
|
||
IN PDUMP_DRIVER_WRITE DriverWriteRoutine,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG DriverTransferSize,
|
||
IN KBUGCHECK_DUMP_IO_TYPE DataType
|
||
);
|
||
|
||
VOID
|
||
IopMapPhysicalMemory(
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG_PTR MemoryAddress,
|
||
IN PPHYSICAL_MEMORY_RUN PhysicalMemoryRun,
|
||
IN ULONG Length
|
||
);
|
||
|
||
NTSTATUS
|
||
IopLoadDumpDriver (
|
||
IN OUT PDUMP_STACK_CONTEXT DumpStack,
|
||
IN PWCHAR DriverNameString,
|
||
IN PWCHAR NewBaseNameString
|
||
);
|
||
|
||
NTSTATUS
|
||
IopInitializeSummaryDump(
|
||
IN OUT PMEMORY_DUMP MemoryDump,
|
||
IN PDUMP_CONTROL_BLOCK DumpControlBlock
|
||
);
|
||
|
||
NTSTATUS
|
||
IopWriteSummaryHeader(
|
||
IN PSUMMARY_DUMP SummaryHeader,
|
||
IN PDUMP_DRIVER_WRITE Write,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG WriteSize,
|
||
IN ULONG Length
|
||
);
|
||
|
||
VOID
|
||
IopMapVirtualToPhysicalMdl(
|
||
IN OUT PMDL pMdl,
|
||
IN ULONG_PTR dwMemoryAddress,
|
||
IN ULONG dwLength
|
||
);
|
||
|
||
ULONG
|
||
IopCreateSummaryDump (
|
||
IN PMEMORY_DUMP MemoryDump
|
||
);
|
||
|
||
VOID
|
||
IopDeleteNonExistentMemory(
|
||
PSUMMARY_DUMP Header,
|
||
PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock
|
||
);
|
||
|
||
NTSTATUS
|
||
IopInvokeSecondaryDumpDataCallbacks(
|
||
IN PDUMP_DRIVER_WRITE DriverWriteRoutine,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG DriverTransferSize,
|
||
IN BYTE* Buffer,
|
||
IN ULONG BufferSize,
|
||
IN ULONG MaxTotal,
|
||
IN ULONG MaxPerCallback,
|
||
OUT OPTIONAL PULONG SpaceNeeded
|
||
);
|
||
|
||
NTSTATUS
|
||
IopInvokeDumpIoCallbacks(
|
||
IN PVOID Buffer,
|
||
IN ULONG BufferSize,
|
||
IN KBUGCHECK_DUMP_IO_TYPE Type
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
IopGetDumpStack (
|
||
IN PWCHAR ModulePrefix,
|
||
OUT PDUMP_STACK_CONTEXT *pDumpStack,
|
||
IN PUNICODE_STRING pUniDeviceName,
|
||
IN PWSTR pDumpDriverName,
|
||
IN DEVICE_USAGE_NOTIFICATION_TYPE UsageType,
|
||
IN ULONG IgnoreDeviceUsageFailure
|
||
);
|
||
|
||
BOOLEAN
|
||
IopInitializeDCB(
|
||
);
|
||
|
||
LARGE_INTEGER
|
||
IopCalculateRequiredDumpSpace(
|
||
IN ULONG dwDmpFlags,
|
||
IN ULONG dwHeaderSize,
|
||
IN PFN_NUMBER dwMaxPages,
|
||
IN PFN_NUMBER dwMaxSummaryPages
|
||
);
|
||
|
||
NTSTATUS
|
||
IopCompleteDumpInitialization(
|
||
IN HANDLE FileHandle
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
VOID
|
||
IopReadDumpRegistry(
|
||
OUT PULONG dumpControl,
|
||
OUT PULONG numberOfHeaderPages,
|
||
OUT PULONG autoReboot,
|
||
OUT PULONG dumpFileSize
|
||
);
|
||
VOID
|
||
IopFreeDCB(
|
||
BOOLEAN FreeDCB
|
||
);
|
||
|
||
#pragma alloc_text(PAGE,IoGetDumpStack)
|
||
#pragma alloc_text(PAGE,IopGetDumpStack)
|
||
#pragma alloc_text(PAGE,IopLoadDumpDriver)
|
||
#pragma alloc_text(PAGE,IoFreeDumpStack)
|
||
#pragma alloc_text(PAGE,IopCompleteDumpInitialization)
|
||
#pragma alloc_text(PAGE,IopFreeDCB)
|
||
#pragma alloc_text(PAGE,IopReadDumpRegistry)
|
||
#pragma alloc_text(PAGE,IopInitializeDCB)
|
||
#pragma alloc_text(PAGE,IopConfigureCrashDump)
|
||
#pragma alloc_text(PAGE,IoInitializeCrashDump)
|
||
#pragma alloc_text(PAGE,IoConfigureCrashDump)
|
||
#endif
|
||
|
||
|
||
#if defined (i386)
|
||
|
||
//
|
||
// Functions
|
||
//
|
||
|
||
|
||
BOOLEAN
|
||
X86PaeEnabled(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Is PAE currently enabled?
|
||
|
||
Return Values:
|
||
|
||
Return TRUE if PAE is enabled in the CR4 register, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Reg_Cr4;
|
||
|
||
_asm {
|
||
_emit 0Fh
|
||
_emit 20h
|
||
_emit 0E0h ;; mov eax, cr4
|
||
mov Reg_Cr4, eax
|
||
}
|
||
|
||
return (Reg_Cr4 & CR4_PAE ? TRUE : FALSE);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
BOOLEAN
|
||
IopIsAddressRangeValid(
|
||
IN PVOID VirtualAddress,
|
||
IN SIZE_T Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validate a range of addresses.
|
||
|
||
Arguments:
|
||
|
||
Virtual Address - Beginning of of memory block to validate.
|
||
|
||
Length - Length of memory block to validate.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Address range is valid.
|
||
|
||
FALSE - Address range is not valid.
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT_PTR Va;
|
||
ULONG Pages;
|
||
|
||
Va = (UINT_PTR) PAGE_ALIGN (VirtualAddress);
|
||
Pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES (VirtualAddress, Length);
|
||
|
||
while (Pages) {
|
||
|
||
if ((Va < 0x10000) ||
|
||
(!MmIsAddressValid ( (LPVOID) Va))) {
|
||
return FALSE;
|
||
}
|
||
|
||
Va += PAGE_SIZE;
|
||
Pages--;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
IoAddTriageDumpDataBlock(
|
||
IN PVOID Address,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Add an entry to the list of data blocks that should
|
||
be saved in any triage dump generated. The entire
|
||
block must be valid for any of it to be saved.
|
||
|
||
Arguments:
|
||
|
||
Address - Beginning of data block.
|
||
|
||
Length - Length of data block. This must be less than
|
||
the triage dump size.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Block was added.
|
||
|
||
FALSE - Block was not added.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PTRIAGE_PTR_DATA_BLOCK Block;
|
||
PUCHAR MinAddress, MaxAddress;
|
||
|
||
if (Length >= TRIAGE_DUMP_SIZE ||
|
||
!IopIsAddressRangeValid(Address, Length)) {
|
||
return FALSE;
|
||
}
|
||
|
||
MinAddress = (PUCHAR)Address;
|
||
MaxAddress = MinAddress + Length;
|
||
|
||
//
|
||
// Minimize overlap between the new block and existing blocks.
|
||
// Blocks cannot simply be merged as blocks are inserted in
|
||
// priority order for storage in the dump. Combining a low-priority
|
||
// block with a high-priority block could lead to a medium-
|
||
// priority block being bumped improperly from the dump.
|
||
//
|
||
|
||
Block = IopTriageDumpDataBlocks;
|
||
for (i = 0; i < IopNumTriageDumpDataBlocks; i++, Block++) {
|
||
|
||
if (MinAddress >= Block->MaxAddress ||
|
||
MaxAddress <= Block->MinAddress) {
|
||
// No overlap.
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Trim overlap out of the new block. If this
|
||
// would split the new block into pieces don't
|
||
// trim to keep things simple. Content may then
|
||
// be duplicated in the dump.
|
||
//
|
||
|
||
if (MinAddress >= Block->MinAddress) {
|
||
if (MaxAddress <= Block->MaxAddress) {
|
||
// New block is completely contained.
|
||
return TRUE;
|
||
}
|
||
|
||
// New block extends above the current block
|
||
// so trim off the low-range overlap.
|
||
MinAddress = Block->MaxAddress;
|
||
} else if (MaxAddress <= Block->MaxAddress) {
|
||
// New block extends below the current block
|
||
// so trim off the high-range overlap.
|
||
MaxAddress = Block->MinAddress;
|
||
}
|
||
}
|
||
|
||
if (IopNumTriageDumpDataBlocks >= IO_MAX_TRIAGE_DUMP_DATA_BLOCKS) {
|
||
return FALSE;
|
||
}
|
||
|
||
Block = IopTriageDumpDataBlocks + IopNumTriageDumpDataBlocks++;
|
||
Block->MinAddress = MinAddress;
|
||
Block->MaxAddress = MaxAddress;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
IopAddRunTimeTriageDataBlocks(
|
||
IN PCONTEXT Context,
|
||
IN PVOID* StackMin,
|
||
IN PVOID* StackMax,
|
||
IN PVOID* StoreMin,
|
||
IN PVOID* StoreMax
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Add data blocks referenced by the context or
|
||
other runtime state.
|
||
|
||
Arguments:
|
||
|
||
Context - Context record at the time the dump is being generated for.
|
||
|
||
StackMin, StackMax - Stack memory boundaries. Stack memory is
|
||
stored elsewhere in the dump.
|
||
|
||
StoreMin, StoreMax - Backing store memory boundaries. Store memory
|
||
is stored elsewhere in the dump.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUSHORT ContextOffset;
|
||
|
||
ContextOffset = IopRunTimeContextOffsets;
|
||
while (*ContextOffset < IOP_LAST_CONTEXT_OFFSET) {
|
||
|
||
PVOID* Ptr;
|
||
|
||
//
|
||
// Retrieve possible pointers from the context
|
||
// registers.
|
||
//
|
||
|
||
Ptr = *(PVOID**)((PUCHAR)Context + *ContextOffset);
|
||
|
||
// Stack and backing store memory is already saved
|
||
// so ignore any pointers that fall into those ranges.
|
||
if ((Ptr < StackMin || Ptr >= StackMax) &&
|
||
(Ptr < StoreMin || Ptr >= StoreMax)) {
|
||
IoAddTriageDumpDataBlock(PAGE_ALIGN(Ptr), PAGE_SIZE);
|
||
}
|
||
|
||
ContextOffset++;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IoGetDumpStack (
|
||
IN PWCHAR ModulePrefix,
|
||
OUT PDUMP_STACK_CONTEXT * pDumpStack,
|
||
IN DEVICE_USAGE_NOTIFICATION_TYPE UsageType,
|
||
IN ULONG IgnoreDeviceUsageFailure
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine loads a dump stack instance and returns an allocated
|
||
context structure to track the loaded dumps stack.
|
||
|
||
Arguments:
|
||
|
||
ModePrefix - The prefix to prepent to BaseName during the load
|
||
operation. This allows loading the same drivers
|
||
multiple times with different virtual names and
|
||
linkages.
|
||
|
||
pDumpStack - The returned dump stack context structure
|
||
|
||
UsageType - The Device Notification Usage Type for this file, that
|
||
this routine will send as to the device object once the
|
||
file has been successfully created and initialized.
|
||
|
||
IgnoreDeviceUsageFailure - If the Device Usage Notification Irp fails, allow
|
||
this to succeed anyway.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
|
||
PAGED_CODE();
|
||
return IopGetDumpStack(ModulePrefix,
|
||
pDumpStack,
|
||
&IoArcBootDeviceName,
|
||
DEFAULT_DUMP_DRIVER,
|
||
UsageType,
|
||
IgnoreDeviceUsageFailure
|
||
);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IoIsTriageDumpEnabled(
|
||
VOID
|
||
)
|
||
{
|
||
if (IopDumpControlBlock &&
|
||
(IopDumpControlBlock->Flags & DCB_TRIAGE_DUMP_ENABLED)) {
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
IopDisplayString(
|
||
IN PCCHAR FormatString,
|
||
...
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Display a string to the boot video console. This will also print the
|
||
string to the debugger, if the proper flags have been enabled.
|
||
|
||
Arguments:
|
||
|
||
String - String to display.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
va_list ap;
|
||
CHAR buffer [ 128 ];
|
||
|
||
va_start( ap, FormatString );
|
||
|
||
_vsnprintf( buffer,
|
||
sizeof ( buffer ),
|
||
FormatString,
|
||
ap );
|
||
|
||
//
|
||
// Display the string to the boot video monitor.
|
||
//
|
||
|
||
InbvDisplayString ( buffer );
|
||
|
||
//
|
||
// And, optionally, to the debugger.
|
||
//
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP [DISP]: %s\r",
|
||
buffer ));
|
||
|
||
va_end(ap);
|
||
|
||
}
|
||
|
||
|
||
typedef struct _INTERNAL_GEOMETRY {
|
||
DISK_GEOMETRY Geometry;
|
||
LARGE_INTEGER DiskSize;
|
||
DISK_PARTITION_INFO PartitionInfo;
|
||
} INTERNAL_GEOMETRY, *PINTERNAL_GEOMETRY;
|
||
|
||
C_ASSERT ( FIELD_OFFSET (INTERNAL_GEOMETRY, PartitionInfo) == FIELD_OFFSET (DISK_GEOMETRY_EX, Data) );
|
||
|
||
|
||
NTSTATUS
|
||
IopGetDumpStack (
|
||
IN PWCHAR ModulePrefix,
|
||
OUT PDUMP_STACK_CONTEXT * DumpStackBuffer,
|
||
IN PUNICODE_STRING UniDeviceName,
|
||
IN PWCHAR DumpDriverName,
|
||
IN DEVICE_USAGE_NOTIFICATION_TYPE UsageType,
|
||
IN ULONG IgnoreDeviceUsageFailure
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine loads a dump stack instance and returns an allocated
|
||
context structure to track the loaded dumps stack.
|
||
|
||
Arguments:
|
||
|
||
ModePrefix - The prefix to prepent to BaseName during the load
|
||
operation. This allows loading the same drivers
|
||
multiple times with different virtual names and
|
||
linkages.
|
||
|
||
DumpStackBuffer - The returned dump stack context structure
|
||
|
||
DeviceName - The name of the target dump device
|
||
|
||
DumpDriverName - The name of the target dump driver
|
||
|
||
UsageType - The Device Notification Usage Type for this file, that
|
||
this routine will send as to the device object once the
|
||
file has been successfully created and initialized.
|
||
|
||
IgnoreDeviceUsageFailure - If the Device Usage Notification Irp fails, allow
|
||
this to succeed anyway.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PDUMP_STACK_CONTEXT DumpStack;
|
||
PUCHAR Buffer;
|
||
ANSI_STRING AnsiString;
|
||
UNICODE_STRING TempName;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
NTSTATUS Status;
|
||
HANDLE DeviceHandle;
|
||
SCSI_ADDRESS ScsiAddress;
|
||
BOOLEAN ScsiDump;
|
||
PARTITION_INFORMATION_EX PartitionInfo;
|
||
PFILE_OBJECT FileObject;
|
||
PDEVICE_OBJECT DeviceObject;
|
||
PDUMP_POINTERS DumpPointers;
|
||
UNICODE_STRING DriverName;
|
||
PDRIVER_OBJECT DriverObject;
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
PWCHAR DumpName;
|
||
PWCHAR NameOffset;
|
||
KEVENT Event;
|
||
PVOID p1;
|
||
PHYSICAL_ADDRESS pa;
|
||
ULONG i;
|
||
IO_STACK_LOCATION irpSp;
|
||
PINTERNAL_GEOMETRY Geometry;
|
||
PDUMP_INITIALIZATION_CONTEXT DumpInit;
|
||
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Prefix:%ws stk: %x device:%ws driver:%ws\n",
|
||
ModulePrefix,
|
||
DumpStackBuffer,
|
||
UniDeviceName->Buffer,
|
||
DumpDriverName
|
||
));
|
||
|
||
ASSERT (DeviceUsageTypeUndefined != UsageType);
|
||
|
||
DumpStack = ExAllocatePool (
|
||
NonPagedPool,
|
||
sizeof (DUMP_STACK_CONTEXT) + sizeof (DUMP_POINTERS)
|
||
);
|
||
|
||
if (!DumpStack) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(DumpStack, sizeof(DUMP_STACK_CONTEXT)+sizeof(DUMP_POINTERS));
|
||
DumpInit = &DumpStack->Init;
|
||
DumpPointers = (PDUMP_POINTERS) (DumpStack + 1);
|
||
DumpStack->DumpPointers = DumpPointers;
|
||
InitializeListHead (&DumpStack->DriverList);
|
||
DumpName = NULL;
|
||
|
||
//
|
||
// Allocate scratch buffer
|
||
//
|
||
|
||
Buffer = ExAllocatePool (PagedPool, PAGE_SIZE);
|
||
if (!Buffer) {
|
||
ExFreePool (DumpStack);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (!KeGetBugMessageText(BUGCODE_PSS_CRASH_INIT, &DumpStack->InitMsg) ||
|
||
!KeGetBugMessageText(BUGCODE_PSS_CRASH_PROGRESS, &DumpStack->ProgMsg) ||
|
||
!KeGetBugMessageText(BUGCODE_PSS_CRASH_DONE, &DumpStack->DoneMsg)) {
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
goto Done;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
UniDeviceName,
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = ZwOpenFile(
|
||
&DeviceHandle,
|
||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
||
&ObjectAttributes,
|
||
&IoStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_NON_DIRECTORY_FILE
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not open boot device partition, %s\n",
|
||
Buffer
|
||
));
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// Check to see whether or not the system was booted from a SCSI device.
|
||
//
|
||
|
||
Status = ZwDeviceIoControlFile (
|
||
DeviceHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatus,
|
||
IOCTL_SCSI_GET_ADDRESS,
|
||
NULL,
|
||
0,
|
||
&ScsiAddress,
|
||
sizeof( SCSI_ADDRESS )
|
||
);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
ZwWaitForSingleObject (
|
||
DeviceHandle,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
Status = IoStatus.Status;
|
||
}
|
||
|
||
ScsiDump = (BOOLEAN) (NT_SUCCESS(Status));
|
||
|
||
//
|
||
// If SCSI then allocate storage to contain the target address information.
|
||
//
|
||
|
||
DumpInit->TargetAddress = NULL;
|
||
|
||
if (ScsiDump) {
|
||
|
||
DumpInit->TargetAddress = ExAllocatePool(
|
||
NonPagedPool,
|
||
sizeof (SCSI_ADDRESS)
|
||
);
|
||
//
|
||
// Formerly, this allocation was allowed to fail and the dump port
|
||
// driver would search for a disk with a matching signature. No
|
||
// longer. If we can't allocate a SCSI address, just fail.
|
||
// Note, if we always pass in a valid SCSI target address, then the
|
||
// disk signature isn't really necessary, but leave it in for now.
|
||
//
|
||
|
||
if (DumpInit->TargetAddress == NULL) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Done;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
DumpInit->TargetAddress,
|
||
&ScsiAddress,
|
||
sizeof(SCSI_ADDRESS)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Determine the disk signature for the device from which the system was
|
||
// booted and get the partition offset.
|
||
//
|
||
|
||
Status = ZwDeviceIoControlFile(
|
||
DeviceHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatus,
|
||
IOCTL_DISK_GET_PARTITION_INFO_EX,
|
||
NULL,
|
||
0,
|
||
&PartitionInfo,
|
||
sizeof( PartitionInfo )
|
||
);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
ZwWaitForSingleObject (
|
||
DeviceHandle,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
Status = IoStatus.Status;
|
||
}
|
||
|
||
//
|
||
// Use the scratch buffer for the geometry.
|
||
//
|
||
|
||
Geometry = (PINTERNAL_GEOMETRY) Buffer;
|
||
|
||
Status = ZwDeviceIoControlFile(
|
||
DeviceHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatus,
|
||
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
|
||
NULL,
|
||
0,
|
||
Geometry,
|
||
sizeof (*Geometry)
|
||
);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
ZwWaitForSingleObject (
|
||
DeviceHandle,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
Status = IoStatus.Status;
|
||
}
|
||
|
||
//
|
||
// Copy the signature, either MBR or GPT.
|
||
//
|
||
|
||
DumpInit->PartitionStyle = Geometry->PartitionInfo.PartitionStyle;
|
||
if ( DumpInit->PartitionStyle == PARTITION_STYLE_MBR ) {
|
||
DumpInit->DiskInfo.Mbr.Signature = Geometry->PartitionInfo.Mbr.Signature;
|
||
DumpInit->DiskInfo.Mbr.CheckSum = Geometry->PartitionInfo.Mbr.CheckSum;
|
||
} else {
|
||
DumpInit->DiskInfo.Gpt.DiskId = Geometry->PartitionInfo.Gpt.DiskId;
|
||
}
|
||
|
||
//
|
||
// The scratch buffer is now free to use.
|
||
//
|
||
Geometry = NULL;
|
||
|
||
//
|
||
// Get the adapter object and base mapping registers for the disk from
|
||
// the disk driver. These will be used to call the HAL once the system
|
||
// system has crashed, since it is not possible at that point to recreate
|
||
// them from scratch.
|
||
//
|
||
|
||
ObReferenceObjectByHandle (
|
||
DeviceHandle,
|
||
0,
|
||
IoFileObjectType,
|
||
KernelMode,
|
||
(PVOID *) &FileObject,
|
||
NULL
|
||
);
|
||
|
||
|
||
DeviceObject = IoGetRelatedDeviceObject (FileObject);
|
||
|
||
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
||
|
||
Irp = IoBuildDeviceIoControlRequest(
|
||
IOCTL_SCSI_GET_DUMP_POINTERS,
|
||
DeviceObject,
|
||
NULL,
|
||
0,
|
||
DumpPointers,
|
||
sizeof (DUMP_POINTERS),
|
||
FALSE,
|
||
&Event,
|
||
&IoStatus
|
||
);
|
||
|
||
if (!Irp) {
|
||
ObDereferenceObject (FileObject);
|
||
ZwClose (DeviceHandle);
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Done;
|
||
}
|
||
|
||
IrpSp = IoGetNextIrpStackLocation (Irp);
|
||
|
||
IrpSp->FileObject = FileObject;
|
||
|
||
Status = IoCallDriver( DeviceObject, Irp );
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||
Status = IoStatus.Status;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status) ||
|
||
IoStatus.Information < FIELD_OFFSET(DUMP_POINTERS, DeviceObject)) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not get dump pointers; error = %x, length %x\n",
|
||
Status,
|
||
IoStatus.Information
|
||
));
|
||
|
||
ObDereferenceObject (FileObject);
|
||
ZwClose (DeviceHandle);
|
||
goto Done;
|
||
}
|
||
DumpStack->PointersLength = (ULONG) IoStatus.Information;
|
||
|
||
//
|
||
// If the driver returned a pointer to a device object, that is the
|
||
// object for the dump driver (non-scsi case)
|
||
//
|
||
|
||
DeviceObject = (PDEVICE_OBJECT) DumpPointers->DeviceObject;
|
||
if (DeviceObject) {
|
||
DriverObject = DeviceObject->DriverObject;
|
||
|
||
//
|
||
// Loop through the name of the driver looking for the end of the name,
|
||
// which is the name of the dump image.
|
||
//
|
||
|
||
DumpName = DriverObject->DriverName.Buffer;
|
||
while ( NameOffset = wcsstr( DumpName, L"\\" )) {
|
||
DumpName = ++NameOffset;
|
||
}
|
||
|
||
ScsiDump = FALSE;
|
||
}
|
||
|
||
//
|
||
// Release the handle, but keep the reference to the file object as it
|
||
// will be needed at free dump dump driver time
|
||
//
|
||
|
||
DumpStack->FileObject = FileObject;
|
||
ZwClose (DeviceHandle);
|
||
|
||
//
|
||
// Fill in some DumpInit results
|
||
//
|
||
|
||
DumpInit->Length = sizeof (DUMP_INITIALIZATION_CONTEXT);
|
||
DumpInit->Reserved = 0;
|
||
DumpInit->StallRoutine = &KeStallExecutionProcessor;
|
||
DumpInit->AdapterObject = DumpPointers->AdapterObject;
|
||
DumpInit->MappedRegisterBase = DumpPointers->MappedRegisterBase;
|
||
DumpInit->PortConfiguration = DumpPointers->DumpData;
|
||
|
||
DumpStack->ModulePrefix = ModulePrefix;
|
||
DumpStack->PartitionOffset = PartitionInfo.StartingOffset;
|
||
DumpStack->UsageType = DeviceUsageTypeUndefined;
|
||
|
||
//
|
||
// The minimum common buffer size is IO_DUMP_COMMON_BUFFER_SIZE (compatability)
|
||
// This is used by the dump driver for SRB extension, CachedExtension, and sense buffer
|
||
//
|
||
if (DumpPointers->CommonBufferSize < IO_DUMP_COMMON_BUFFER_SIZE) {
|
||
DumpPointers->CommonBufferSize = IO_DUMP_COMMON_BUFFER_SIZE;
|
||
}
|
||
DumpInit->CommonBufferSize = DumpPointers->CommonBufferSize;
|
||
|
||
//
|
||
// Allocate the required common buffers
|
||
//
|
||
|
||
if (DumpPointers->AllocateCommonBuffers) {
|
||
pa.QuadPart = 0x1000000 - 1;
|
||
|
||
for (i=0; i < 2; i++) {
|
||
|
||
if (DumpInit->AdapterObject) {
|
||
|
||
p1 = (*((PDMA_ADAPTER)DumpInit->AdapterObject)->DmaOperations->
|
||
AllocateCommonBuffer)(
|
||
(PDMA_ADAPTER)DumpInit->AdapterObject,
|
||
DumpPointers->CommonBufferSize,
|
||
&pa,
|
||
FALSE
|
||
);
|
||
|
||
} else {
|
||
|
||
p1 = MmAllocateContiguousMemory (
|
||
DumpPointers->CommonBufferSize,
|
||
pa
|
||
);
|
||
|
||
if (!p1) {
|
||
p1 = MmAllocateNonCachedMemory (DumpPointers->CommonBufferSize);
|
||
}
|
||
pa = MmGetPhysicalAddress(p1);
|
||
}
|
||
|
||
if (!p1) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not allocate common buffers for dump\n"
|
||
));
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Done;
|
||
}
|
||
|
||
DumpInit->CommonBuffer[i] = p1;
|
||
DumpInit->PhysicalAddress[i] = pa;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Determine whether or not the system booted from SCSI.
|
||
//
|
||
|
||
if (ScsiDump) {
|
||
|
||
//
|
||
// Load the boot disk and port driver to be used by the various
|
||
// miniports for writing memory to the disk.
|
||
//
|
||
|
||
Status = IopLoadDumpDriver (
|
||
DumpStack,
|
||
DumpDriverName,
|
||
SCSIPORT_DRIVER_NAME
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
IopLogErrorEvent(0,9,STATUS_SUCCESS,IO_DUMP_DRIVER_LOAD_FAILURE,0,NULL,0,NULL);
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// The disk and port dump driver has been loaded. Load the appropriate
|
||
// miniport driver as well so that the boot device can be accessed.
|
||
//
|
||
|
||
DriverName.Length = 0;
|
||
DriverName.Buffer = (PVOID) Buffer;
|
||
DriverName.MaximumLength = PAGE_SIZE;
|
||
|
||
|
||
//
|
||
// The system was booted from SCSI. Get the name of the appropriate
|
||
// miniport driver and load it.
|
||
//
|
||
|
||
sprintf(Buffer, "\\Device\\ScsiPort%d", ScsiAddress.PortNumber );
|
||
RtlInitAnsiString( &AnsiString, Buffer );
|
||
RtlAnsiStringToUnicodeString( &TempName, &AnsiString, TRUE );
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&TempName,
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = ZwOpenFile(
|
||
&DeviceHandle,
|
||
FILE_READ_ATTRIBUTES,
|
||
&ObjectAttributes,
|
||
&IoStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_NON_DIRECTORY_FILE
|
||
);
|
||
|
||
RtlFreeUnicodeString( &TempName );
|
||
if (!NT_SUCCESS( Status )) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not open SCSI port %d, error = %x\n",
|
||
ScsiAddress.PortNumber,
|
||
Status
|
||
));
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// Convert the file handle into a pointer to the device object, and
|
||
// get the name of the driver from its driver object.
|
||
//
|
||
|
||
ObReferenceObjectByHandle(
|
||
DeviceHandle,
|
||
0,
|
||
IoFileObjectType,
|
||
KernelMode,
|
||
(PVOID *) &FileObject,
|
||
NULL
|
||
);
|
||
|
||
DriverObject = FileObject->DeviceObject->DriverObject;
|
||
ObDereferenceObject( FileObject );
|
||
ZwClose( DeviceHandle );
|
||
//
|
||
// Loop through the name of the driver looking for the end of the name,
|
||
// which is the name of the miniport image.
|
||
//
|
||
|
||
DumpName = DriverObject->DriverName.Buffer;
|
||
while ( NameOffset = wcsstr( DumpName, L"\\" )) {
|
||
DumpName = ++NameOffset;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Load the dump driver
|
||
//
|
||
|
||
if (!DumpName) {
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
goto Done;
|
||
}
|
||
|
||
swprintf ((PWCHAR) Buffer, L"\\SystemRoot\\System32\\Drivers\\%s.sys", DumpName);
|
||
Status = IopLoadDumpDriver (
|
||
DumpStack,
|
||
(PWCHAR) Buffer,
|
||
NULL
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
IopLogErrorEvent(0,10,STATUS_SUCCESS,IO_DUMP_DRIVER_LOAD_FAILURE,0,NULL,0,NULL);
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// Claim the file as part of specific device usage path.
|
||
//
|
||
|
||
FileObject = DumpStack->FileObject;
|
||
DeviceObject = IoGetRelatedDeviceObject (FileObject);
|
||
|
||
RtlZeroMemory (&irpSp, sizeof (IO_STACK_LOCATION));
|
||
|
||
irpSp.MajorFunction = IRP_MJ_PNP;
|
||
irpSp.MinorFunction = IRP_MN_DEVICE_USAGE_NOTIFICATION;
|
||
irpSp.Parameters.UsageNotification.Type = UsageType;
|
||
irpSp.Parameters.UsageNotification.InPath = TRUE;
|
||
irpSp.FileObject = FileObject;
|
||
|
||
Status = IopSynchronousCall (DeviceObject, &irpSp, NULL);
|
||
|
||
if (!NT_SUCCESS(Status) && IgnoreDeviceUsageFailure) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_WARNING,
|
||
"CRASHDUMP: IopGetDumpStack: DEVICE_USAGE_NOTIFICATION "
|
||
"Error ignored (%x)\n",
|
||
Status
|
||
));
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
DumpStack->UsageType = UsageType;
|
||
}
|
||
|
||
Done:
|
||
if (NT_SUCCESS(Status)) {
|
||
*DumpStackBuffer = DumpStack;
|
||
} else {
|
||
IoFreeDumpStack (DumpStack);
|
||
}
|
||
ExFreePool (Buffer);
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IopLoadDumpDriver (
|
||
IN OUT PDUMP_STACK_CONTEXT DumpStack,
|
||
IN PWCHAR DriverNameString,
|
||
IN PWCHAR NewBaseNameString OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Worker function for IoGetDumpStack to load a particular driver into
|
||
the current DumpStack being created
|
||
|
||
Arguments:
|
||
|
||
DumpStack - Dump driver stack being built
|
||
|
||
DriverNameString - The string name of the driver to load
|
||
|
||
NewBaseNameString - The modified basename of the driver once loaded
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PDUMP_STACK_IMAGE DumpImage;
|
||
UNICODE_STRING DriverName;
|
||
UNICODE_STRING BaseName;
|
||
UNICODE_STRING Prefix;
|
||
PUNICODE_STRING LoadBaseName;
|
||
|
||
//
|
||
// Allocate space to track this dump driver
|
||
//
|
||
|
||
DumpImage = ExAllocatePool(
|
||
NonPagedPool,
|
||
sizeof (DUMP_STACK_IMAGE)
|
||
);
|
||
|
||
if (!DumpImage) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Load the system image
|
||
//
|
||
|
||
RtlInitUnicodeString (&DriverName, DriverNameString);
|
||
RtlInitUnicodeString (&Prefix, DumpStack->ModulePrefix);
|
||
LoadBaseName = NULL;
|
||
if (NewBaseNameString) {
|
||
LoadBaseName = &BaseName;
|
||
RtlInitUnicodeString (&BaseName, NewBaseNameString);
|
||
BaseName.MaximumLength = Prefix.Length + BaseName.Length;
|
||
BaseName.Buffer = ExAllocatePool (
|
||
NonPagedPool,
|
||
BaseName.MaximumLength
|
||
);
|
||
|
||
|
||
if (!BaseName.Buffer) {
|
||
ExFreePool (DumpImage);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
BaseName.Length = 0;
|
||
RtlAppendUnicodeStringToString (&BaseName, &Prefix);
|
||
RtlAppendUnicodeToString (&BaseName, NewBaseNameString);
|
||
}
|
||
else {
|
||
BaseName.Buffer = NULL;
|
||
}
|
||
|
||
Status = MmLoadSystemImage(
|
||
&DriverName,
|
||
&Prefix,
|
||
LoadBaseName,
|
||
MM_LOAD_IMAGE_AND_LOCKDOWN,
|
||
&DumpImage->Image,
|
||
&DumpImage->ImageBase
|
||
);
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: MmLoadAndLockSystemImage\n"
|
||
" DumpImage %p Image %p Base %p\n",
|
||
DumpImage,
|
||
DumpImage->Image,
|
||
DumpImage->ImageBase
|
||
));
|
||
|
||
if (BaseName.Buffer) {
|
||
ExFreePool (BaseName.Buffer);
|
||
}
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not load %wZ; error = %x\n",
|
||
&DriverName,
|
||
Status
|
||
));
|
||
|
||
ExFreePool (DumpImage);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Put this driver on the list of drivers to be processed at crash time
|
||
//
|
||
|
||
DumpImage->SizeOfImage = DumpImage->Image->SizeOfImage;
|
||
InsertTailList (&DumpStack->DriverList, &DumpImage->Link);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
ULONG
|
||
IopGetDumpControlBlockCheck (
|
||
IN PDUMP_CONTROL_BLOCK Dcb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Return the current checksum total for the Dcb
|
||
|
||
Arguments:
|
||
|
||
DumpStack - Dump driver stack to checksum
|
||
|
||
Return Value:
|
||
|
||
Checksum value
|
||
|
||
--*/
|
||
{
|
||
ULONG Check;
|
||
PLIST_ENTRY Link;
|
||
PDUMP_STACK_IMAGE DumpImage;
|
||
PMAPPED_ADDRESS MappedAddress;
|
||
PDUMP_STACK_CONTEXT DumpStack;
|
||
|
||
//
|
||
// Check the DCB, memory descriptor array, and the FileDescriptorArray
|
||
//
|
||
|
||
Check = PoSimpleCheck(0, Dcb, sizeof(DUMP_CONTROL_BLOCK));
|
||
|
||
Check = PoSimpleCheck(Check, Dcb->FileDescriptorArray, Dcb->FileDescriptorSize);
|
||
|
||
DumpStack = Dcb->DumpStack;
|
||
if (DumpStack) {
|
||
|
||
//
|
||
// Include the dump stack context structure, and dump driver images
|
||
//
|
||
|
||
Check = PoSimpleCheck(Check, DumpStack, sizeof(DUMP_STACK_CONTEXT));
|
||
Check = PoSimpleCheck(Check, DumpStack->DumpPointers, DumpStack->PointersLength);
|
||
|
||
for (Link = DumpStack->DriverList.Flink;
|
||
Link != &DumpStack->DriverList;
|
||
Link = Link->Flink) {
|
||
|
||
DumpImage = CONTAINING_RECORD(Link, DUMP_STACK_IMAGE, Link);
|
||
Check = PoSimpleCheck(Check, DumpImage, sizeof(DUMP_STACK_IMAGE));
|
||
|
||
#if !defined (_IA64_)
|
||
|
||
//
|
||
// ISSUE - 2000/02/14 - math: Add image check image for IA64.
|
||
//
|
||
// Disable the image checksum on IA64 because it's broken.
|
||
|
||
|
||
Check = PoSimpleCheck(Check, DumpImage->ImageBase, DumpImage->SizeOfImage);
|
||
#endif
|
||
|
||
}
|
||
|
||
//
|
||
// Include the mapped addresses
|
||
//
|
||
// If this is non-null it is treated as a PMAPPED_ADDRESS * (see scsiport and atdisk)
|
||
//
|
||
if (DumpStack->Init.MappedRegisterBase != NULL) {
|
||
MappedAddress = *(PMAPPED_ADDRESS *)DumpStack->Init.MappedRegisterBase;
|
||
} else {
|
||
MappedAddress = NULL;
|
||
}
|
||
|
||
while (MappedAddress) {
|
||
Check = PoSimpleCheck (Check, MappedAddress, sizeof(MAPPED_ADDRESS));
|
||
MappedAddress = MappedAddress->NextMappedAddress;
|
||
}
|
||
}
|
||
|
||
return Check;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IoInitializeDumpStack (
|
||
IN PDUMP_STACK_CONTEXT DumpStack,
|
||
IN PUCHAR MessageBuffer OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize the dump driver stack referenced by DumpStack to perform IO.
|
||
|
||
Arguments:
|
||
|
||
DumpStack - Dump driver stack being initialized
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
|
||
PDUMP_INITIALIZATION_CONTEXT DumpInit;
|
||
PLIST_ENTRY Link;
|
||
NTSTATUS Status;
|
||
PDRIVER_INITIALIZE DriverInit;
|
||
PDUMP_STACK_IMAGE DumpImage;
|
||
|
||
|
||
DumpInit = &DumpStack->Init;
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Verify checksum on DumpStack structure
|
||
//
|
||
|
||
//
|
||
// Initializes the dump drivers
|
||
//
|
||
|
||
for (Link = DumpStack->DriverList.Flink;
|
||
Link != &DumpStack->DriverList;
|
||
Link = Link->Flink) {
|
||
|
||
DumpImage = CONTAINING_RECORD(Link, DUMP_STACK_IMAGE, Link);
|
||
|
||
//
|
||
// Call this driver's driver init. Only the first driver gets the
|
||
// dump initialization context
|
||
//
|
||
|
||
DriverInit = (PDRIVER_INITIALIZE) (ULONG_PTR) DumpImage->Image->EntryPoint;
|
||
Status = DriverInit (NULL, (PUNICODE_STRING) DumpInit);
|
||
DumpInit = NULL;
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Unable to initialize driver; error = %x\n",
|
||
Status
|
||
));
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
DumpInit = &DumpStack->Init;
|
||
|
||
//
|
||
// Display string we are starting
|
||
//
|
||
|
||
if (MessageBuffer) {
|
||
IopDisplayString ( MessageBuffer );
|
||
}
|
||
|
||
//
|
||
// Open the partition from which the system was booted.
|
||
// This returns TRUE if the disk w/the appropriate signature was found,
|
||
// otherwise a NULL, in which case there is no way to continue.
|
||
//
|
||
|
||
if (!DumpInit->OpenRoutine (DumpStack->PartitionOffset)) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not find/open partition offset\n"
|
||
));
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
IoGetDumpHiberRanges (
|
||
IN PVOID HiberContext,
|
||
IN PDUMP_STACK_CONTEXT DumpStack
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds the dump driver stack storage to the hibernate range list,
|
||
to inform the hibernate procedure which pages need cloned,
|
||
discarded or not checksumed as they are in use by the dump
|
||
stack.
|
||
|
||
Arguments:
|
||
|
||
HiberContext - Pointer to the hiber context structure
|
||
|
||
DumpStack - Dump driver stack being initialized
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PDUMP_POINTERS DumpPointers;
|
||
PDUMP_STACK_IMAGE DumpImage;
|
||
PLIST_ENTRY Link;
|
||
|
||
DumpPointers = DumpStack->DumpPointers;
|
||
|
||
//
|
||
// Report the common buffer
|
||
//
|
||
|
||
if (DumpPointers->CommonBufferVa) {
|
||
PoSetHiberRange (
|
||
HiberContext,
|
||
PO_MEM_CL_OR_NCHK,
|
||
DumpPointers->CommonBufferVa,
|
||
DumpPointers->CommonBufferSize,
|
||
'fubc'
|
||
);
|
||
}
|
||
|
||
//
|
||
// Dump the entire image of the dump drivers
|
||
//
|
||
|
||
for (Link = DumpStack->DriverList.Flink;
|
||
Link != &DumpStack->DriverList;
|
||
Link = Link->Flink) {
|
||
|
||
DumpImage = CONTAINING_RECORD(Link, DUMP_STACK_IMAGE, Link);
|
||
|
||
PoSetHiberRange (
|
||
HiberContext,
|
||
PO_MEM_CL_OR_NCHK,
|
||
DumpImage->ImageBase,
|
||
DumpImage->SizeOfImage,
|
||
'gmID'
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
IoFreeDumpStack (
|
||
IN PDUMP_STACK_CONTEXT DumpStack
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free the dump driver stack referenced by DumpStack
|
||
|
||
Arguments:
|
||
|
||
DumpStack - Dump driver stack being initialized
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PDUMP_INITIALIZATION_CONTEXT DumpInit;
|
||
PDUMP_STACK_IMAGE DumpImage;
|
||
PDEVICE_OBJECT DeviceObject;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
PIRP Irp;
|
||
KEVENT Event;
|
||
NTSTATUS Status;
|
||
ULONG i;
|
||
PFILE_OBJECT FileObject;
|
||
IO_STACK_LOCATION irpSp;
|
||
|
||
PAGED_CODE();
|
||
DumpInit = &DumpStack->Init;
|
||
|
||
//
|
||
// Release the claim to this file as a specific device usage path.
|
||
//
|
||
|
||
FileObject = DumpStack->FileObject;
|
||
if (FileObject) {
|
||
DeviceObject = IoGetRelatedDeviceObject (FileObject);
|
||
|
||
RtlZeroMemory (&irpSp, sizeof (IO_STACK_LOCATION));
|
||
|
||
irpSp.MajorFunction = IRP_MJ_PNP;
|
||
irpSp.MinorFunction = IRP_MN_DEVICE_USAGE_NOTIFICATION;
|
||
irpSp.Parameters.UsageNotification.Type = DumpStack->UsageType;
|
||
irpSp.Parameters.UsageNotification.InPath = FALSE;
|
||
irpSp.FileObject = FileObject;
|
||
|
||
if (DeviceUsageTypeUndefined != DumpStack->UsageType) {
|
||
Status = IopSynchronousCall (DeviceObject, &irpSp, NULL);
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free any common buffers which where allocated
|
||
//
|
||
|
||
for (i=0; i < 2; i++) {
|
||
if (DumpInit->CommonBuffer[i]) {
|
||
if (DumpInit->AdapterObject) {
|
||
|
||
(*((PDMA_ADAPTER)DumpInit->AdapterObject)->DmaOperations->
|
||
FreeCommonBuffer )(
|
||
(PDMA_ADAPTER)DumpInit->AdapterObject,
|
||
((PDUMP_POINTERS)DumpStack->DumpPointers)->CommonBufferSize,
|
||
DumpInit->PhysicalAddress[i],
|
||
DumpInit->CommonBuffer[i],
|
||
FALSE
|
||
);
|
||
} else {
|
||
MmFreeContiguousMemory (DumpInit->CommonBuffer[i]);
|
||
}
|
||
}
|
||
DumpInit->CommonBuffer[i] = NULL;
|
||
}
|
||
|
||
//
|
||
// Unload the dump drivers
|
||
//
|
||
|
||
while (!IsListEmpty(&DumpStack->DriverList)) {
|
||
DumpImage = CONTAINING_RECORD(DumpStack->DriverList.Blink, DUMP_STACK_IMAGE, Link);
|
||
RemoveEntryList (&DumpImage->Link);
|
||
MmUnloadSystemImage (DumpImage->Image);
|
||
ExFreePool (DumpImage);
|
||
}
|
||
|
||
//
|
||
// Inform the driver stack that the dump registartion is over
|
||
//
|
||
|
||
if (DumpStack->FileObject) {
|
||
DeviceObject = IoGetRelatedDeviceObject ((PFILE_OBJECT) DumpStack->FileObject);
|
||
|
||
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
||
Irp = IoBuildDeviceIoControlRequest(
|
||
IOCTL_SCSI_FREE_DUMP_POINTERS,
|
||
DeviceObject,
|
||
DumpStack->DumpPointers,
|
||
sizeof (DUMP_POINTERS),
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&Event,
|
||
&IoStatus
|
||
);
|
||
|
||
IrpSp = IoGetNextIrpStackLocation (Irp);
|
||
IrpSp->FileObject = DumpStack->FileObject;
|
||
|
||
Status = IoCallDriver( DeviceObject, Irp );
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||
Status = IoStatus.Status;
|
||
}
|
||
ObDereferenceObject( DumpStack->FileObject );
|
||
}
|
||
//
|
||
// Free the target address if it exists
|
||
//
|
||
if (DumpStack->Init.TargetAddress) {
|
||
ExFreePool( DumpStack->Init.TargetAddress);
|
||
}
|
||
//
|
||
// Free the dump stack context
|
||
//
|
||
|
||
ExFreePool (DumpStack);
|
||
}
|
||
|
||
VOID
|
||
IopGetSecondaryDumpDataLimits(
|
||
ULONG Flags,
|
||
OUT PULONG MaxData,
|
||
OUT PULONG MaxPerCallback
|
||
)
|
||
{
|
||
// When the selected dump type is small also
|
||
// limit the amount of secondary dump data.
|
||
// This prevents overzealous secondary dumpers from
|
||
// creating multi-megabyte secondary dumps when triage
|
||
// dumps are selected.
|
||
if (!(Flags & DCB_DUMP_ENABLED) ||
|
||
(Flags & DCB_DUMP_HEADER_ENABLED)) {
|
||
*MaxData = 0;
|
||
*MaxPerCallback = 0;
|
||
} else if (Flags & DCB_TRIAGE_DUMP_ENABLED) {
|
||
*MaxData = 16 * PAGE_SIZE;
|
||
*MaxPerCallback = PAGE_SIZE;
|
||
} else {
|
||
// Arbitrarily limit maximum data amount to 256MB.
|
||
// There shouldn't be any reason that callers should
|
||
// have anywhere near that much data that wouldn't
|
||
// get picked up by a full dump.
|
||
*MaxData = 256 * 1024 * 1024;
|
||
*MaxPerCallback = *MaxData / 4;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
IopGetSecondaryDumpDataSpace(
|
||
IN PDUMP_CONTROL_BLOCK dcb,
|
||
OUT PULONG Space
|
||
)
|
||
{
|
||
ULONG MaxDumpData;
|
||
ULONG MaxPerCallbackDumpData;
|
||
NTSTATUS NtStatus;
|
||
|
||
IopGetSecondaryDumpDataLimits(dcb->Flags,
|
||
&MaxDumpData, &MaxPerCallbackDumpData);
|
||
|
||
NtStatus = IopInvokeSecondaryDumpDataCallbacks(NULL, NULL, NULL, 0,
|
||
(PBYTE)dcb->HeaderPage,
|
||
PAGE_SIZE,
|
||
MaxDumpData,
|
||
MaxPerCallbackDumpData,
|
||
Space);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
*Space = 0;
|
||
}
|
||
|
||
return NtStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IopInitializeDumpSpaceAndType(
|
||
IN PDUMP_CONTROL_BLOCK dcb,
|
||
IN OUT PMEMORY_DUMP MemoryDump,
|
||
IN ULONG SecondarySpace
|
||
)
|
||
{
|
||
LARGE_INTEGER Space;
|
||
|
||
Space.QuadPart = 0;
|
||
|
||
if (dcb->Flags & DCB_TRIAGE_DUMP_ENABLED) {
|
||
|
||
//
|
||
// Fixed size dump for triage-dumps.
|
||
//
|
||
|
||
MemoryDump->Header.DumpType = DUMP_TYPE_TRIAGE;
|
||
MemoryDump->Header.MiniDumpFields = dcb->TriageDumpFlags;
|
||
Space.QuadPart = TRIAGE_DUMP_SIZE;
|
||
|
||
|
||
} else if (dcb->Flags & DCB_SUMMARY_DUMP_ENABLED) {
|
||
|
||
MemoryDump->Header.DumpType = DUMP_TYPE_SUMMARY;
|
||
Space = IopCalculateRequiredDumpSpace(
|
||
dcb->Flags,
|
||
dcb->HeaderSize,
|
||
MmPhysicalMemoryBlock->NumberOfPages,
|
||
MemoryDump->Summary.Pages
|
||
);
|
||
} else {
|
||
|
||
if (dcb->Flags & DCB_DUMP_HEADER_ENABLED) {
|
||
MemoryDump->Header.DumpType = DUMP_TYPE_HEADER;
|
||
}
|
||
|
||
Space = IopCalculateRequiredDumpSpace(
|
||
dcb->Flags,
|
||
dcb->HeaderSize,
|
||
MmPhysicalMemoryBlock->NumberOfPages,
|
||
MmPhysicalMemoryBlock->NumberOfPages
|
||
);
|
||
}
|
||
|
||
//
|
||
// Add in any secondary space.
|
||
//
|
||
|
||
Space.QuadPart += SecondarySpace;
|
||
|
||
//
|
||
// If the calculated size is larger than the pagefile, truncate it to
|
||
// the pagefile size.
|
||
//
|
||
|
||
if (Space.QuadPart > dcb->DumpFileSize.QuadPart) {
|
||
Space.QuadPart = dcb->DumpFileSize.QuadPart;
|
||
}
|
||
|
||
MemoryDump->Header.RequiredDumpSpace = Space;
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Dump File Size set to %I64x\n",
|
||
Space.QuadPart
|
||
));
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IoWriteCrashDump(
|
||
IN ULONG BugCheckCode,
|
||
IN ULONG_PTR BugCheckParameter1,
|
||
IN ULONG_PTR BugCheckParameter2,
|
||
IN ULONG_PTR BugCheckParameter3,
|
||
IN ULONG_PTR BugCheckParameter4,
|
||
IN PVOID ContextSave,
|
||
IN PKTHREAD Thread,
|
||
OUT PBOOLEAN Reboot
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks to see whether or not crash dumps are enabled and, if
|
||
so, writes all of physical memory to the system disk's paging file.
|
||
|
||
Arguments:
|
||
|
||
BugCheckCode/ParameterN - Code and parameters w/which BugCheck was called.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PDUMP_CONTROL_BLOCK dcb;
|
||
PDUMP_STACK_CONTEXT dumpStack;
|
||
PDUMP_DRIVER_WRITE write;
|
||
PDUMP_DRIVER_FINISH finishUp;
|
||
PDUMP_HEADER header;
|
||
PCONTEXT context = ContextSave;
|
||
PMEMORY_DUMP MemoryDump;
|
||
LARGE_INTEGER diskByteOffset;
|
||
PPFN_NUMBER page;
|
||
PFN_NUMBER localMdl[(sizeof( MDL )/sizeof(PFN_NUMBER)) + 17];
|
||
PMDL mdl;
|
||
PLARGE_INTEGER mcb;
|
||
ULONG_PTR memoryAddress;
|
||
ULONG byteOffset;
|
||
ULONG byteCount;
|
||
ULONG bytesRemaining;
|
||
PFN_NUMBER ActualPages;
|
||
ULONG dwTransferSize;
|
||
PFN_NUMBER NumberOfPages;
|
||
#if defined (_X86_)
|
||
ULONG_PTR DirBasePage;
|
||
#endif
|
||
ULONG MaxDumpData;
|
||
ULONG MaxPerCallbackDumpData;
|
||
NTSTATUS SecondaryStatus;
|
||
ULONG SecondarySpace;
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
ASSERT (Reboot != NULL);
|
||
|
||
//
|
||
// Initialization
|
||
//
|
||
|
||
MemoryDump = NULL;
|
||
|
||
//
|
||
// Immediately fill out the reboot parameter as auto-reboot
|
||
// may be enabled even with no other post-mortem features.
|
||
//
|
||
|
||
if (IopAutoReboot) {
|
||
*Reboot = TRUE;
|
||
} else {
|
||
*Reboot = FALSE;
|
||
}
|
||
|
||
//
|
||
// Begin by determining whether or not crash dumps are enabled. If not,
|
||
// return immediately since there is nothing to do.
|
||
//
|
||
|
||
dcb = IopDumpControlBlock;
|
||
if (!dcb) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (dcb->Flags & DCB_DUMP_ENABLED || dcb->Flags & DCB_SUMMARY_ENABLED) {
|
||
|
||
IopFinalCrashDumpStatus = STATUS_PENDING;
|
||
|
||
//
|
||
// A dump is to be written to the paging file. Ensure that all of the
|
||
// descriptor data for what needs to be done is valid, otherwise it
|
||
// could be that part of the reason for the bugcheck is that this data
|
||
// was corrupted. Or, it could be that no paging file was found yet,
|
||
// or any number of other situations.
|
||
//
|
||
|
||
//
|
||
// We do not check the checksum if IopIgnoreDumpCheck is TRUE. Use
|
||
// this to make debugging easier.
|
||
//
|
||
|
||
if (!IopIgnoreDumpCheck &&
|
||
IopGetDumpControlBlockCheck(dcb) != IopDumpControlBlockChecksum) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Disk dump routine returning due to DCB integrity error\n"
|
||
" Computed Checksum: %d != Saved Checksum %d\n"
|
||
" No dump will be created\n",
|
||
IopGetDumpControlBlockCheck (dcb),
|
||
IopDumpControlBlockChecksum
|
||
));
|
||
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Message that we are starting the crashdump
|
||
//
|
||
|
||
dumpStack = dcb->DumpStack;
|
||
|
||
// sprintf( messageBuffer, "%Z\n", &dumpStack->InitMsg );
|
||
//
|
||
// Disable HAL Verifier during a crash dump.
|
||
//
|
||
VfDisableHalVerifier();
|
||
|
||
//
|
||
// Initialize the dump stack
|
||
//
|
||
|
||
status = IoInitializeDumpStack (dumpStack, NULL);
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If we successfully initialized the dump stack, print out the PSS
|
||
// message.
|
||
//
|
||
|
||
IopDisplayString ("%Z\n", &dumpStack->InitMsg);
|
||
|
||
//
|
||
// Record the dump driver's entry points.
|
||
//
|
||
|
||
write = dumpStack->Init.WriteRoutine;
|
||
finishUp = dumpStack->Init.FinishRoutine;
|
||
|
||
|
||
dwTransferSize = dumpStack->Init.MaximumTransferSize;
|
||
|
||
if ( ( !dwTransferSize ) || ( dwTransferSize > IO_DUMP_MAXIMUM_TRANSFER_SIZE ) ) {
|
||
dwTransferSize = IO_DUMP_MINIMUM_TRANSFER_SIZE;
|
||
}
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Maximum Transfer Size = %x\n",dwTransferSize
|
||
));
|
||
|
||
//
|
||
// The boot partition was found, so put together a dump file header
|
||
// and write it to the disk.
|
||
//
|
||
|
||
// Get the amount of secondary dump data while the
|
||
// header page can still be used as scratch space.
|
||
SecondaryStatus = IopGetSecondaryDumpDataSpace(dcb, &SecondarySpace);
|
||
|
||
MemoryDump = (PMEMORY_DUMP) dcb->HeaderPage;
|
||
header = &MemoryDump->Header;
|
||
|
||
RtlFillMemoryUlong( header, sizeof(*header), DUMP_SIGNATURE );
|
||
header->ValidDump = DUMP_VALID_DUMP;
|
||
header->BugCheckCode = BugCheckCode;
|
||
header->BugCheckParameter1 = BugCheckParameter1;
|
||
header->BugCheckParameter2 = BugCheckParameter2;
|
||
header->BugCheckParameter3 = BugCheckParameter3;
|
||
header->BugCheckParameter4 = BugCheckParameter4;
|
||
header->SecondaryDataState = (ULONG)SecondaryStatus;
|
||
|
||
#if defined (_X86_)
|
||
|
||
//
|
||
// Add the current page directory table page - don't use the directory
|
||
// table base for the crashing process as we have switched cr3 on
|
||
// stack overflow crashes, etc.
|
||
//
|
||
|
||
_asm {
|
||
mov eax, cr3
|
||
mov DirBasePage, eax
|
||
}
|
||
header->DirectoryTableBase = DirBasePage;
|
||
|
||
#elif defined (_IA64_)
|
||
ASSERT (((MmSystemParentTablePage << PAGE_SHIFT) >> PAGE_SHIFT) ==
|
||
MmSystemParentTablePage);
|
||
header->DirectoryTableBase = MmSystemParentTablePage << PAGE_SHIFT;
|
||
#else
|
||
header->DirectoryTableBase = KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0];
|
||
#endif
|
||
header->PfnDataBase = (ULONG_PTR)MmPfnDatabase;
|
||
header->PsLoadedModuleList = (ULONG_PTR) &PsLoadedModuleList;
|
||
header->PsActiveProcessHead = (ULONG_PTR) &PsActiveProcessHead;
|
||
header->NumberProcessors = dcb->NumberProcessors;
|
||
header->MajorVersion = dcb->MajorVersion;
|
||
header->MinorVersion = dcb->MinorVersion;
|
||
|
||
#if defined (i386)
|
||
header->PaeEnabled = X86PaeEnabled ();
|
||
#endif
|
||
header->KdDebuggerDataBlock = KdGetDataBlock();
|
||
|
||
header->MachineImageType = CURRENT_IMAGE_TYPE ();
|
||
|
||
if (!(dcb->Flags & DCB_DUMP_ENABLED)) {
|
||
NumberOfPages = 1;
|
||
} else {
|
||
NumberOfPages = MmPhysicalMemoryBlock->NumberOfPages;
|
||
}
|
||
|
||
strcpy( header->VersionUser, dcb->VersionUser );
|
||
|
||
//
|
||
// Copy the physical memory descriptor.
|
||
//
|
||
|
||
RtlCopyMemory (&MemoryDump->Header.PhysicalMemoryBlock,
|
||
MmPhysicalMemoryBlock,
|
||
sizeof( PHYSICAL_MEMORY_DESCRIPTOR ) +
|
||
((MmPhysicalMemoryBlock->NumberOfRuns - 1) *
|
||
sizeof( PHYSICAL_MEMORY_RUN )) );
|
||
|
||
RtlCopyMemory( MemoryDump->Header.ContextRecord,
|
||
context,
|
||
sizeof( CONTEXT ) );
|
||
|
||
MemoryDump->Header.Exception.ExceptionCode = STATUS_BREAKPOINT;
|
||
MemoryDump->Header.Exception.ExceptionRecord = 0;
|
||
MemoryDump->Header.Exception.NumberParameters = 0;
|
||
MemoryDump->Header.Exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
||
MemoryDump->Header.Exception.ExceptionAddress = PROGRAM_COUNTER (context);
|
||
|
||
//
|
||
// Init dump type to FULL
|
||
//
|
||
|
||
MemoryDump->Header.DumpType = DUMP_TYPE_FULL;
|
||
|
||
//
|
||
// Save the System time and uptime (This is always available)
|
||
// It's a KSYSTEM_TIME structure, but we only store the low and
|
||
// high 1 part
|
||
//
|
||
|
||
MemoryDump->Header.SystemTime.LowPart = SharedUserData->SystemTime.LowPart;
|
||
MemoryDump->Header.SystemTime.HighPart = SharedUserData->SystemTime.High1Time;
|
||
|
||
MemoryDump->Header.SystemUpTime.LowPart = SharedUserData->InterruptTime.LowPart;
|
||
MemoryDump->Header.SystemUpTime.HighPart = SharedUserData->InterruptTime.High1Time;
|
||
|
||
// Save product type and suite.
|
||
MemoryDump->Header.ProductType = SharedUserData->NtProductType;
|
||
MemoryDump->Header.SuiteMask = SharedUserData->SuiteMask;
|
||
|
||
//
|
||
// Set the Required dump size in the dump header. In the case of
|
||
// a summary dump the file allocation size can be significantly larger
|
||
// then the amount of used space.
|
||
//
|
||
|
||
MemoryDump->Header.RequiredDumpSpace.QuadPart = 0;
|
||
|
||
IopGetSecondaryDumpDataLimits(dcb->Flags,
|
||
&MaxDumpData, &MaxPerCallbackDumpData);
|
||
if (MaxDumpData > SecondarySpace) {
|
||
MaxDumpData = SecondarySpace;
|
||
if (MaxPerCallbackDumpData > MaxDumpData) {
|
||
MaxPerCallbackDumpData = MaxDumpData;
|
||
}
|
||
}
|
||
|
||
if (dcb->Flags & DCB_DUMP_ENABLED) {
|
||
|
||
//
|
||
// If summary dump try to create the dump header
|
||
//
|
||
|
||
if ( (dcb->Flags & DCB_SUMMARY_DUMP_ENABLED) ) {
|
||
|
||
//
|
||
// Initialize the summary dump
|
||
//
|
||
|
||
status = IopInitializeSummaryDump( MemoryDump, dcb );
|
||
|
||
if ( !NT_SUCCESS (status) ) {
|
||
|
||
//
|
||
// No summary dump header so return.
|
||
//
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_WARNING,
|
||
"CRASHDUMP: NULL summary dump header\n"
|
||
));
|
||
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
IopInitializeDumpSpaceAndType ( dcb, MemoryDump, SecondarySpace );
|
||
}
|
||
|
||
//
|
||
// All of the pieces of the header file have been generated. Before
|
||
// mapping or writing anything to the disk, the I- & D-stream caches
|
||
// must be flushed so that page color coherency is kept. Sweep both
|
||
// caches now.
|
||
//
|
||
|
||
KeSweepCurrentDcache();
|
||
KeSweepCurrentIcache();
|
||
|
||
//
|
||
// Create MDL for dump.
|
||
//
|
||
|
||
mdl = (PMDL) &localMdl[0];
|
||
MmCreateMdl( mdl, NULL, PAGE_SIZE );
|
||
mdl->MdlFlags |= MDL_PAGES_LOCKED;
|
||
|
||
mcb = dcb->FileDescriptorArray;
|
||
|
||
page = MmGetMdlPfnArray(mdl);
|
||
*page = dcb->HeaderPfn;
|
||
mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
|
||
|
||
bytesRemaining = PAGE_SIZE;
|
||
memoryAddress = (ULONG_PTR) dcb->HeaderPage;
|
||
|
||
IopInvokeDumpIoCallbacks(dcb->HeaderPage, PAGE_SIZE,
|
||
KbDumpIoHeader);
|
||
|
||
//
|
||
// All of the pieces of the header file have been generated. Write
|
||
// the header page to the paging file, using the appropriate drivers,
|
||
// etc.
|
||
//
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Writing dump header to disk\n"
|
||
));
|
||
|
||
while (bytesRemaining) {
|
||
|
||
if (mcb[0].QuadPart <= bytesRemaining) {
|
||
byteCount = mcb[0].LowPart;
|
||
} else {
|
||
byteCount = bytesRemaining;
|
||
}
|
||
|
||
mdl->ByteCount = byteCount;
|
||
mdl->ByteOffset = (ULONG)(memoryAddress & (PAGE_SIZE - 1));
|
||
mdl->MappedSystemVa = (PVOID) memoryAddress;
|
||
mdl->StartVa = PAGE_ALIGN ((PVOID)memoryAddress);
|
||
|
||
//
|
||
// Write to disk.
|
||
//
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
if (!NT_SUCCESS( write( &mcb[1], mdl ) )) {
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Adjust bytes remaining.
|
||
//
|
||
|
||
bytesRemaining -= byteCount;
|
||
memoryAddress += byteCount;
|
||
mcb[0].QuadPart = mcb[0].QuadPart - byteCount;
|
||
mcb[1].QuadPart = mcb[1].QuadPart + byteCount;
|
||
|
||
if (!mcb[0].QuadPart) {
|
||
mcb += 2;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If only requesting a header dump, we are now done.
|
||
//
|
||
|
||
if (dcb->Flags & DCB_DUMP_HEADER_ENABLED) {
|
||
goto FinishDump;
|
||
}
|
||
|
||
//
|
||
// The header page has been written. If this is a triage-dump, write
|
||
// the dump information and bail. Otherwise, fall through and do the
|
||
// full or summary dump.
|
||
//
|
||
|
||
if (dcb->Flags & DCB_TRIAGE_DUMP_ENABLED) {
|
||
status = IopWriteTriageDump (dcb->TriageDumpFlags,
|
||
write,
|
||
&mcb,
|
||
mdl,
|
||
dwTransferSize,
|
||
context,
|
||
Thread,
|
||
dcb->TriageDumpBuffer,
|
||
dcb->TriageDumpBufferSize - sizeof(DUMP_HEADER),
|
||
dcb->BuildNumber,
|
||
(UCHAR)dcb->Flags
|
||
);
|
||
|
||
if (!NT_SUCCESS (status)) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_WARNING,
|
||
"CRASHDUMP: Failed to write triage-dump\n"
|
||
));
|
||
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
return FALSE;
|
||
}
|
||
|
||
goto FinishDump;
|
||
}
|
||
|
||
//
|
||
// The header page has been written to the paging file. If a full dump
|
||
// of all of physical memory is to be written, write it now.
|
||
//
|
||
|
||
if (dcb->Flags & DCB_DUMP_ENABLED) {
|
||
|
||
ULONG64 bytesDoneSoFar = 0;
|
||
ULONG currentPercentage = 0;
|
||
ULONG maximumPercentage = 0;
|
||
|
||
|
||
//
|
||
// Actual Pages is the number of pages to dump.
|
||
//
|
||
|
||
ActualPages = NumberOfPages;
|
||
|
||
if (dcb->Flags & DCB_SUMMARY_DUMP_ENABLED) {
|
||
|
||
//
|
||
// At this point the dump header header has been sucessfully
|
||
// written. Write the summary dump header.
|
||
//
|
||
|
||
status = IopWriteSummaryHeader(
|
||
&MemoryDump->Summary,
|
||
write,
|
||
&mcb,
|
||
mdl,
|
||
dwTransferSize,
|
||
(dcb->HeaderSize - sizeof(DUMP_HEADER))
|
||
);
|
||
|
||
if ( !NT_SUCCESS (status) ) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_WARNING,
|
||
"CRASHDUMP: Error writing summary dump header %08x\n",
|
||
status
|
||
));
|
||
|
||
IopFinalCrashDumpStatus = status;
|
||
return FALSE;
|
||
}
|
||
|
||
ActualPages = MemoryDump->Summary.Pages;
|
||
|
||
}
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Writing Memory Dump\n"
|
||
));
|
||
|
||
//
|
||
// Set the virtual file offset and initialize loop variables and
|
||
// constants.
|
||
//
|
||
|
||
memoryAddress = (ULONG_PTR)MmPhysicalMemoryBlock->Run[0].BasePage * PAGE_SIZE;
|
||
|
||
if ( dcb->Flags & DCB_SUMMARY_DUMP_ENABLED ) {
|
||
|
||
status = IopWriteSummaryDump (
|
||
(PRTL_BITMAP) &MemoryDump->Summary.Bitmap,
|
||
write,
|
||
&dumpStack->ProgMsg,
|
||
NULL,
|
||
&mcb,
|
||
dwTransferSize
|
||
);
|
||
|
||
if (!NT_SUCCESS (status)) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_WARNING,
|
||
"CRASHDUMP: Failed to write kernel memory dump\n"
|
||
));
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
return FALSE;
|
||
}
|
||
goto FinishDump;
|
||
}
|
||
|
||
//
|
||
// Now loop, writing all of physical memory to the paging file.
|
||
//
|
||
|
||
while (mcb[0].QuadPart) {
|
||
|
||
diskByteOffset = mcb[1];
|
||
|
||
//
|
||
// Calculate byte offset;
|
||
//
|
||
|
||
byteOffset = (ULONG)(memoryAddress & (PAGE_SIZE - 1));
|
||
|
||
if (dwTransferSize <= mcb[0].QuadPart) {
|
||
byteCount = dwTransferSize - byteOffset;
|
||
} else {
|
||
byteCount = mcb[0].LowPart;
|
||
}
|
||
if ((ULONG64)ActualPages * PAGE_SIZE - bytesDoneSoFar <
|
||
byteCount) {
|
||
byteCount = (ULONG)
|
||
((ULONG64)ActualPages * PAGE_SIZE - bytesDoneSoFar);
|
||
}
|
||
bytesDoneSoFar += byteCount;
|
||
|
||
currentPercentage = (ULONG)
|
||
(((bytesDoneSoFar / PAGE_SIZE) * 100) / ActualPages);
|
||
|
||
if (currentPercentage > maximumPercentage) {
|
||
|
||
maximumPercentage = currentPercentage;
|
||
|
||
//
|
||
// Update message on screen.
|
||
//
|
||
|
||
IopDisplayString ( "%Z: %3d\r",
|
||
&dumpStack->ProgMsg,
|
||
maximumPercentage
|
||
);
|
||
}
|
||
|
||
//
|
||
// Map the physical memory and write it to the
|
||
// current segment of the file.
|
||
//
|
||
|
||
IopMapPhysicalMemory( mdl,
|
||
memoryAddress,
|
||
&MmPhysicalMemoryBlock->Run[0],
|
||
byteCount
|
||
);
|
||
|
||
//
|
||
// Write the next segment.
|
||
//
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
IopInvokeDumpIoCallbacks((PUCHAR)mdl->MappedSystemVa +
|
||
mdl->ByteOffset, byteCount,
|
||
KbDumpIoBody);
|
||
|
||
if (!NT_SUCCESS( write( &diskByteOffset, mdl ) )) {
|
||
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Adjust pointers for next part.
|
||
//
|
||
|
||
memoryAddress += byteCount;
|
||
mcb[0].QuadPart = mcb[0].QuadPart - byteCount;
|
||
mcb[1].QuadPart = mcb[1].QuadPart + byteCount;
|
||
|
||
if (!mcb[0].QuadPart) {
|
||
mcb += 2;
|
||
}
|
||
|
||
if ((bytesDoneSoFar / PAGE_SIZE) >= ActualPages) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: memory dump written\n"
|
||
));
|
||
}
|
||
|
||
FinishDump:
|
||
|
||
IopDisplayString ( "%Z", &dumpStack->DoneMsg );
|
||
|
||
IopInvokeSecondaryDumpDataCallbacks(write,
|
||
&mcb,
|
||
mdl,
|
||
dwTransferSize,
|
||
(PBYTE)dcb->HeaderPage,
|
||
PAGE_SIZE,
|
||
MaxDumpData,
|
||
MaxPerCallbackDumpData,
|
||
NULL);
|
||
|
||
// Final I/O complete notification.
|
||
IopInvokeDumpIoCallbacks(NULL, 0, KbDumpIoComplete);
|
||
|
||
//
|
||
// Sweep the cache so the debugger will work.
|
||
//
|
||
|
||
KeSweepCurrentDcache();
|
||
KeSweepCurrentIcache();
|
||
|
||
//
|
||
// Have the dump flush the adapter and disk caches.
|
||
//
|
||
|
||
finishUp();
|
||
|
||
//
|
||
// Indicate to the debugger that the dump has been successfully
|
||
// written.
|
||
//
|
||
|
||
IopFinalCrashDumpStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
IopMapPhysicalMemory(
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG_PTR MemoryAddress,
|
||
IN PPHYSICAL_MEMORY_RUN PhysicalMemoryRun,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to fill in the specified MDL (Memory Descriptor
|
||
List) w/the appropriate information to map the specified memory address
|
||
range.
|
||
|
||
Arguments:
|
||
|
||
Mdl - Address of the MDL to be filled in.
|
||
|
||
MemoryAddress - Pseudo-virtual address being mapped.
|
||
|
||
PhysicalMemoryRun - Base address of the physical memory run list.
|
||
|
||
Length - Length of transfer to be mapped.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPHYSICAL_MEMORY_RUN pmr = PhysicalMemoryRun;
|
||
PPFN_NUMBER page;
|
||
PFN_NUMBER pages;
|
||
PFN_NUMBER base;
|
||
PFN_NUMBER currentBase;
|
||
|
||
//
|
||
// Begin by determining the base physical page of the start of the address
|
||
// range and filling in the MDL appropriately.
|
||
//
|
||
Mdl->StartVa = PAGE_ALIGN( (PVOID) (MemoryAddress) );
|
||
Mdl->ByteOffset = (ULONG)(MemoryAddress & (PAGE_SIZE - 1));
|
||
Mdl->ByteCount = Length;
|
||
|
||
//
|
||
// Get the page frame index for the base address.
|
||
//
|
||
|
||
base = (PFN_NUMBER) ((ULONG_PTR)(Mdl->StartVa) >> PAGE_SHIFT);
|
||
pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MemoryAddress, Length);
|
||
currentBase = pmr->BasePage;
|
||
page = MmGetMdlPfnArray(Mdl);
|
||
|
||
//
|
||
// Map all of the pages for this transfer until there are no more remaining
|
||
// to be mapped.
|
||
//
|
||
|
||
while (pages) {
|
||
|
||
//
|
||
// Find the memory run that maps the beginning of this transfer.
|
||
//
|
||
|
||
while (currentBase + pmr->PageCount <= base) {
|
||
currentBase += pmr->PageCount;
|
||
pmr++;
|
||
}
|
||
|
||
//
|
||
// The current memory run maps the start of this transfer. Capture
|
||
// the base page for the start of the transfer.
|
||
//
|
||
|
||
*page++ = pmr->BasePage + (PFN_NUMBER)(base++ - currentBase);
|
||
pages--;
|
||
}
|
||
|
||
//
|
||
// All of the PFNs for the address range have been filled in so map the
|
||
// physical memory into virtual address space.
|
||
//
|
||
|
||
MmMapMemoryDumpMdl( Mdl );
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
IopAddPageToPageMap(
|
||
IN ULONG MaxPage,
|
||
IN PRTL_BITMAP BitMap,
|
||
IN ULONG PageFrameIndex,
|
||
IN ULONG NumberOfPages
|
||
)
|
||
{
|
||
//
|
||
// Sometimes we get PFNs that are out of range. Just ignore them.
|
||
//
|
||
|
||
if (PageFrameIndex >= MaxPage) {
|
||
return;
|
||
}
|
||
|
||
RtlSetBits (BitMap, PageFrameIndex, NumberOfPages);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
IopRemovePageFromPageMap(
|
||
IN ULONG MaxPage,
|
||
IN PRTL_BITMAP BitMap,
|
||
IN ULONG PageFrameIndex,
|
||
IN ULONG NumberOfPages
|
||
)
|
||
{
|
||
//
|
||
// Sometimes we get PFNs that are out of range. Just ignore them.
|
||
//
|
||
|
||
if (PageFrameIndex >= MaxPage) {
|
||
return;
|
||
}
|
||
|
||
RtlClearBits (BitMap, PageFrameIndex, NumberOfPages);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IoSetDumpRange(
|
||
IN PMM_KERNEL_DUMP_CONTEXT Context,
|
||
IN PVOID StartVa,
|
||
IN ULONG_PTR Pages,
|
||
IN ULONG AddressFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine includes this range of memory in the dump
|
||
|
||
Arguments:
|
||
|
||
Context - Dump context.
|
||
|
||
StartVa - Starting virtual address.
|
||
|
||
Pages - The number of pages to include
|
||
|
||
AddressFlags - 0 if the address is virtually mapped.
|
||
1 if the address is super/large page mapped. This implies
|
||
the entire page range is physically contiguous.
|
||
2 if the address really represents a physical page frame
|
||
number. This also implies the entire page range is
|
||
physically contiguous.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - On success.
|
||
|
||
NTSTATUS - Error.
|
||
|
||
--*/
|
||
{
|
||
PCHAR Va;
|
||
PRTL_BITMAP BitMap;
|
||
PHYSICAL_ADDRESS PhyAddr;
|
||
PSUMMARY_DUMP Summary;
|
||
BOOLEAN AllPagesSet;
|
||
ULONG_PTR PageFrameIndex;
|
||
|
||
//
|
||
// Validation
|
||
//
|
||
|
||
ASSERT (Context != NULL &&
|
||
Context->Context != NULL);
|
||
|
||
//
|
||
// Initialization
|
||
//
|
||
|
||
Summary = (PSUMMARY_DUMP) Context->Context;
|
||
BitMap = (PRTL_BITMAP) &Summary->Bitmap;
|
||
Va = StartVa;
|
||
AllPagesSet = TRUE;
|
||
|
||
//
|
||
// Win64 can have really large page addresses. This dump code does
|
||
// not handle that yet. Note that before this assert is removed
|
||
// the casts of Pages to ULONG must be removed.
|
||
//
|
||
|
||
ASSERT(Pages <= MAXULONG);
|
||
|
||
if (AddressFlags == 1) {
|
||
|
||
PhyAddr = MmGetPhysicalAddress (Va);
|
||
IopAddPageToPageMap ( Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG) (PhyAddr.QuadPart >> PAGE_SHIFT),
|
||
(ULONG) Pages
|
||
);
|
||
|
||
} else if (AddressFlags == 2) {
|
||
|
||
PageFrameIndex = (ULONG_PTR) Va;
|
||
|
||
IopAddPageToPageMap ( Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG) PageFrameIndex,
|
||
(ULONG) Pages
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not physically contiguous.
|
||
//
|
||
|
||
while (Pages) {
|
||
|
||
//
|
||
// Only do a translation for valid pages.
|
||
//
|
||
|
||
if ( MmIsAddressValid(Va) ) {
|
||
|
||
//
|
||
// Get the physical mapping. Note: this does not require a lock
|
||
//
|
||
|
||
PhyAddr = MmGetPhysicalAddress (Va);
|
||
|
||
IopAddPageToPageMap ( Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG)( PhyAddr.QuadPart >> PAGE_SHIFT),
|
||
1);
|
||
|
||
if (PhyAddr.QuadPart >> PAGE_SHIFT > Summary->BitmapSize) {
|
||
AllPagesSet = FALSE;
|
||
}
|
||
}
|
||
|
||
Va += PAGE_SIZE;
|
||
Pages--;
|
||
}
|
||
}
|
||
|
||
if (AllPagesSet) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
return STATUS_INVALID_ADDRESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IoFreeDumpRange(
|
||
IN PMM_KERNEL_DUMP_CONTEXT Context,
|
||
IN PVOID StartVa,
|
||
IN ULONG_PTR Pages,
|
||
IN ULONG AddressFlags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine excludes this range of memory in the dump.
|
||
|
||
Arguments:
|
||
|
||
DumpContext - dump context
|
||
|
||
StartVa - Starting VA
|
||
|
||
Pages - The number of pages to include
|
||
|
||
AddressFlags - 0 if the address is virtually mapped.
|
||
1 if the address is super/large page mapped. This implies
|
||
the entire page range is physically contiguous.
|
||
2 if the address really represents a physical page frame
|
||
number. This also implies the entire page range is
|
||
physically contiguous.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - On success.
|
||
|
||
NTSTATUS - Error.
|
||
|
||
--*/
|
||
{
|
||
PCHAR Va;
|
||
PRTL_BITMAP BitMap;
|
||
PHYSICAL_ADDRESS PhyAddr;
|
||
PSUMMARY_DUMP Summary;
|
||
ULONG_PTR PageFrameIndex;
|
||
|
||
ASSERT (Context != NULL &&
|
||
Context->Context != NULL);
|
||
|
||
//
|
||
// Round to page size.
|
||
//
|
||
|
||
Summary = (PSUMMARY_DUMP)Context->Context;
|
||
BitMap = (PRTL_BITMAP) &Summary->Bitmap;
|
||
Va = StartVa;
|
||
|
||
//
|
||
// Win64 can have really large page addresses. This dump code does
|
||
// not handle that yet. Note that before this assert is removed
|
||
// the casts of Pages to ULONG must be removed.
|
||
//
|
||
|
||
ASSERT (Pages <= MAXULONG);
|
||
|
||
if (AddressFlags == 1) {
|
||
|
||
PhyAddr = MmGetPhysicalAddress(Va);
|
||
|
||
IopRemovePageFromPageMap (Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG)(PhyAddr.QuadPart >> PAGE_SHIFT),
|
||
(ULONG) Pages
|
||
);
|
||
|
||
} else if (AddressFlags == 2) {
|
||
|
||
PageFrameIndex = (ULONG_PTR) Va;
|
||
|
||
IopRemovePageFromPageMap (Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG) PageFrameIndex,
|
||
(ULONG) Pages
|
||
);
|
||
} else {
|
||
|
||
while (Pages) {
|
||
|
||
//
|
||
// Only do a translation for valid pages.
|
||
//
|
||
|
||
if ( MmIsAddressValid (Va) ) {
|
||
PhyAddr = MmGetPhysicalAddress (Va);
|
||
|
||
IopRemovePageFromPageMap (Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG)(PhyAddr.QuadPart >> PAGE_SHIFT),
|
||
1);
|
||
|
||
}
|
||
|
||
Va += PAGE_SIZE;
|
||
Pages--;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
LARGE_INTEGER
|
||
IopCalculateRequiredDumpSpace(
|
||
IN ULONG dwDmpFlags,
|
||
IN ULONG dwHeaderSize,
|
||
IN PFN_NUMBER dwMaxPages,
|
||
IN PFN_NUMBER dwMaxSummaryPages
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to calcuate required dump space
|
||
|
||
1. Crash dump summary must be at least 1 page in length.
|
||
|
||
2. Summary dump must be large enough for kernel memory plus header,
|
||
plus summary header.
|
||
|
||
3. Full dump must be large enough for header plus all physical memory.
|
||
|
||
Arguments:
|
||
|
||
dwDmpFlags - Dump Control Block (DCB) flags.
|
||
|
||
dwHeaderSize - The size of the dump header.
|
||
|
||
dwMaxPages - All physical memory.
|
||
|
||
dwMaxSummaryPages - Maximum pages in summary dump.
|
||
|
||
Return Value:
|
||
|
||
Size of the dump file
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER maxMemorySize;
|
||
|
||
//
|
||
// Dump header or dump summary.
|
||
//
|
||
|
||
if ( (dwDmpFlags & DCB_DUMP_HEADER_ENABLED) ||
|
||
( !( dwDmpFlags & DCB_DUMP_ENABLED ) &&
|
||
( dwDmpFlags & DCB_SUMMARY_ENABLED ) ) ) {
|
||
|
||
maxMemorySize.QuadPart = IO_DUMP_MINIMUM_FILE_SIZE;
|
||
return maxMemorySize;
|
||
}
|
||
|
||
if (dwDmpFlags & DCB_TRIAGE_DUMP_ENABLED) {
|
||
|
||
maxMemorySize.QuadPart = TRIAGE_DUMP_SIZE;
|
||
return maxMemorySize;
|
||
}
|
||
|
||
if (dwDmpFlags & DCB_SUMMARY_DUMP_ENABLED) {
|
||
ULONG dwGB;
|
||
|
||
maxMemorySize.QuadPart = (dwMaxSummaryPages) * PAGE_SIZE;
|
||
|
||
//
|
||
// If biased then max kernel memory is 1GB otherwise it is 2GB
|
||
//
|
||
|
||
dwGB = 1024 * 1024 * 1024;
|
||
|
||
if (maxMemorySize.QuadPart > (2 * dwGB) ) {
|
||
if (MmVirtualBias) {
|
||
maxMemorySize.QuadPart = dwGB;
|
||
} else {
|
||
maxMemorySize.QuadPart = (2 * dwGB);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Control block header size for summary dump
|
||
// includes space for the base header, the summary
|
||
// header and the page bitmap.
|
||
//
|
||
|
||
maxMemorySize.QuadPart += dwHeaderSize;
|
||
|
||
return maxMemorySize;
|
||
|
||
}
|
||
|
||
//
|
||
// Full memory dump is #pages * pagesize plus 1 page for the dump header.
|
||
//
|
||
|
||
maxMemorySize.QuadPart = (dwMaxPages * PAGE_SIZE) + dwHeaderSize;
|
||
|
||
return maxMemorySize;
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Triage-dump support routines.
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
IopGetLoadedDriverInfo(
|
||
OUT ULONG * lpDriverCount,
|
||
OUT ULONG * lpSizeOfStringData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get information about all loaded drivers.
|
||
|
||
Arguments:
|
||
|
||
lpDriverCount - Buffer to return the count of all the drivers that are
|
||
currently loaded in the system.
|
||
|
||
lpSizeOfStringData - Buffer to return the sum of the sizes of all driver
|
||
name strings (FullDllName). This does not include the size
|
||
of the UNICODE_STRING structure or a trailing NULL byte.
|
||
|
||
Return Values:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG DriverCount = 0;
|
||
ULONG SizeOfStringData = 0;
|
||
PLIST_ENTRY NextEntry;
|
||
PKLDR_DATA_TABLE_ENTRY DriverEntry;
|
||
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
|
||
DriverEntry = CONTAINING_RECORD (NextEntry,
|
||
KLDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks
|
||
);
|
||
|
||
if (!IopIsAddressRangeValid (DriverEntry, sizeof (*DriverEntry)) ||
|
||
!IopIsAddressRangeValid (DriverEntry->BaseDllName.Buffer,
|
||
DriverEntry->BaseDllName.Length)) {
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
DriverCount++;
|
||
|
||
//
|
||
// The extra two bytes is for the NULL termination. The extra 7 is
|
||
// because we force 8-byte alignment of all strings.
|
||
//
|
||
|
||
SizeOfStringData += DriverEntry->BaseDllName.Length + 2 + 7;
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
|
||
*lpDriverCount = DriverCount;
|
||
*lpSizeOfStringData = SizeOfStringData;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#define DmpPoolStringSize(DumpString)\
|
||
(sizeof (DUMP_STRING) + sizeof (WCHAR) * ( DumpString->Length + 1 ))
|
||
|
||
#define DmpNextPoolString(DumpString) \
|
||
(PDUMP_STRING) ( \
|
||
ALIGN_UP_POINTER( \
|
||
((LPBYTE) DumpString) + DmpPoolStringSize (DumpString), \
|
||
ULONGLONG \
|
||
) \
|
||
)
|
||
|
||
#define ALIGN_8(_x) ALIGN_UP(_x, DWORDLONG)
|
||
|
||
#define ASSERT_ALIGNMENT(Pointer, Alignment)\
|
||
ASSERT ((((ULONG_PTR)Pointer) & ((Alignment) - 1)) == 0)
|
||
|
||
#ifndef IndexByByte
|
||
#define IndexByByte(Pointer, Index) (&(((BYTE*) (Pointer)) [Index]))
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
IopWriteDriverList(
|
||
IN ULONG_PTR BufferAddress,
|
||
IN ULONG BufferSize,
|
||
IN ULONG DriverListOffset,
|
||
IN ULONG StringPoolOffset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write the triage dump driver list to the buffer.
|
||
|
||
Arguments:
|
||
|
||
BufferAddress - The address of the buffer.
|
||
|
||
BufferSize - The size of the buffer.
|
||
|
||
DriverListOffset - The offset within the buffer where the driver list
|
||
should be written.
|
||
|
||
StringPoolOffset - The offset within the buffer where the driver list's
|
||
string pool should start. If there are no other strings for the triage
|
||
dump other than driver name strings, this will be the string pool
|
||
offset.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i = 0;
|
||
PLIST_ENTRY NextEntry;
|
||
PKLDR_DATA_TABLE_ENTRY DriverEntry;
|
||
PDUMP_DRIVER_ENTRY DumpImageArray;
|
||
PDUMP_STRING DumpStringName = NULL;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
|
||
ASSERT (DriverListOffset != 0);
|
||
ASSERT (StringPoolOffset != 0);
|
||
|
||
UNREFERENCED_PARAMETER (BufferSize);
|
||
|
||
DumpImageArray = (PDUMP_DRIVER_ENTRY) (BufferAddress + DriverListOffset);
|
||
DumpStringName = (PDUMP_STRING) (BufferAddress + StringPoolOffset);
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
|
||
DriverEntry = CONTAINING_RECORD (NextEntry,
|
||
KLDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
//
|
||
// Verify the memory is valid before reading anything from it.
|
||
//
|
||
|
||
if (!IopIsAddressRangeValid (DriverEntry, sizeof (*DriverEntry)) ||
|
||
!IopIsAddressRangeValid (DriverEntry->BaseDllName.Buffer,
|
||
DriverEntry->BaseDllName.Length)) {
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Build the entry in the string pool. We guarantee all strings are
|
||
// NULL terminated as well as length prefixed.
|
||
//
|
||
|
||
DumpStringName->Length = DriverEntry->BaseDllName.Length / 2;
|
||
RtlCopyMemory (DumpStringName->Buffer,
|
||
DriverEntry->BaseDllName.Buffer,
|
||
DumpStringName->Length * sizeof (WCHAR)
|
||
);
|
||
|
||
DumpStringName->Buffer[ DumpStringName->Length ] = '\000';
|
||
|
||
RtlCopyMemory (&DumpImageArray [i].LdrEntry,
|
||
DriverEntry,
|
||
sizeof (DumpImageArray [i].LdrEntry)
|
||
);
|
||
|
||
//
|
||
// Add the time/date stamp.
|
||
//
|
||
|
||
DumpImageArray[i].LdrEntry.TimeDateStamp = 0;
|
||
DumpImageArray[i].LdrEntry.SizeOfImage = 0;
|
||
|
||
if ( MmIsAddressValid (DriverEntry->DllBase ) ) {
|
||
|
||
NtHeaders = RtlImageNtHeader (DriverEntry->DllBase);
|
||
ASSERT ( NtHeaders );
|
||
DumpImageArray[i].LdrEntry.TimeDateStamp =
|
||
NtHeaders->FileHeader.TimeDateStamp;
|
||
DumpImageArray[i].LdrEntry.SizeOfImage =
|
||
NtHeaders->OptionalHeader.SizeOfImage;
|
||
|
||
} else if (DriverEntry->Flags & LDRP_NON_PAGED_DEBUG_INFO) {
|
||
|
||
DumpImageArray[i].LdrEntry.TimeDateStamp =
|
||
DriverEntry->NonPagedDebugInfo->TimeDateStamp;
|
||
DumpImageArray[i].LdrEntry.SizeOfImage =
|
||
DriverEntry->NonPagedDebugInfo->SizeOfImage;
|
||
}
|
||
|
||
DumpImageArray [i].DriverNameOffset =
|
||
(ULONG)((ULONG_PTR) DumpStringName - BufferAddress);
|
||
|
||
i++;
|
||
DumpStringName = DmpNextPoolString (DumpStringName);
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ULONG
|
||
IopSizeTriageDumpDataBlocks(
|
||
PTRIAGE_DUMP TriageDump,
|
||
ULONG Offset,
|
||
ULONG BufferSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine all triage dump data blocks that fit and
|
||
update dump header to match.
|
||
|
||
Arguments:
|
||
|
||
TriageDump - Dump header.
|
||
|
||
Offset - Current offset in dump buffer.
|
||
|
||
BufferSize - Dump buffer size.
|
||
|
||
Return Values:
|
||
|
||
Updated offset.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG Size;
|
||
PTRIAGE_PTR_DATA_BLOCK Block;
|
||
|
||
TriageDump->DataBlocksCount = 0;
|
||
|
||
Block = IopTriageDumpDataBlocks;
|
||
for (i = 0; i < IopNumTriageDumpDataBlocks; i++, Block++) {
|
||
Size = ALIGN_8(sizeof(TRIAGE_DATA_BLOCK)) +
|
||
ALIGN_8((ULONG)(Block->MaxAddress - Block->MinAddress));
|
||
if (Offset + Size >= BufferSize) {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
break;
|
||
}
|
||
|
||
if (i == 0) {
|
||
TriageDump->DataBlocksOffset = Offset;
|
||
}
|
||
|
||
Offset += Size;
|
||
TriageDump->DataBlocksCount++;
|
||
}
|
||
|
||
return Offset;
|
||
}
|
||
|
||
VOID
|
||
IopWriteTriageDumpDataBlocks(
|
||
PTRIAGE_DUMP TriageDump,
|
||
PUCHAR BufferAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write triage dump data blocks given in header.
|
||
|
||
Arguments:
|
||
|
||
TriageDump - Dump header.
|
||
|
||
BufferAddress - Address of dump data buffer.
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PTRIAGE_PTR_DATA_BLOCK Block;
|
||
PUCHAR DataBuffer;
|
||
PTRIAGE_DATA_BLOCK DumpBlock;
|
||
|
||
DumpBlock = (PTRIAGE_DATA_BLOCK)
|
||
(BufferAddress + TriageDump->DataBlocksOffset);
|
||
DataBuffer = (PUCHAR)(DumpBlock + TriageDump->DataBlocksCount);
|
||
|
||
Block = IopTriageDumpDataBlocks;
|
||
for (i = 0; i < TriageDump->DataBlocksCount; i++, Block++) {
|
||
|
||
DumpBlock->Address = (ULONG64)(LONG_PTR)Block->MinAddress;
|
||
DumpBlock->Offset = (ULONG)(DataBuffer - BufferAddress);
|
||
DumpBlock->Size = (ULONG)(Block->MaxAddress - Block->MinAddress);
|
||
|
||
RtlCopyMemory(DataBuffer, Block->MinAddress, DumpBlock->Size);
|
||
|
||
DataBuffer += DumpBlock->Size;
|
||
DumpBlock++;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IopWriteTriageDump(
|
||
IN ULONG Fields,
|
||
IN PDUMP_DRIVER_WRITE DriverWriteRoutine,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG DriverTransferSize,
|
||
IN PCONTEXT Context,
|
||
IN PKTHREAD Thread,
|
||
IN BYTE* Buffer,
|
||
IN ULONG BufferSize,
|
||
IN ULONG ServicePackBuild,
|
||
IN ULONG TriageOptions
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write the Triage-Dump to the MCB.
|
||
|
||
Arguments:
|
||
|
||
Fields - The set of fields that should be written.
|
||
|
||
DriverWriteRoutine - The write routine for the driver.
|
||
|
||
Mcb - Message Control Block where the data is to be written.
|
||
|
||
Mdl - A MDL descrbing the data to be written (??).
|
||
|
||
DriverTransferSize - The maximum transfer size for the driver.
|
||
|
||
Context - The context.
|
||
|
||
Buffer - The buffer to use as a scratch buffer.
|
||
|
||
BufferSize - The size of the buffer.
|
||
|
||
ServicePackBuild - Service Pack BuildNumber.
|
||
|
||
TriageOptions - Triage Options.
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - On success.
|
||
|
||
NTSTATUS - Otherwise.
|
||
|
||
Comments:
|
||
|
||
This function assumes that exactly one header page was written.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG SizeOfSection;
|
||
ULONG SizeOfStringData;
|
||
ULONG DriverCount = 0;
|
||
LPVOID Address = NULL;
|
||
ULONG BytesToWrite = 0;
|
||
ULONG_PTR BufferAddress = 0;
|
||
NTSTATUS Status;
|
||
ULONG Offset;
|
||
PTRIAGE_DUMP TriageDump = NULL;
|
||
|
||
//
|
||
// Setup the triage-dump header.
|
||
//
|
||
|
||
if (BufferSize < sizeof (TRIAGE_DUMP) + sizeof (DWORD)) {
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
TriageDump = (PTRIAGE_DUMP) Buffer;
|
||
RtlZeroMemory (TriageDump, sizeof (*TriageDump));
|
||
|
||
//
|
||
// The normal dump header is a DUMP_HEADER.
|
||
//
|
||
|
||
TriageDump->SizeOfDump = BufferSize + sizeof(DUMP_HEADER);
|
||
|
||
//
|
||
// Adjust the BufferSize so we can write the final status DWORD at the
|
||
// end.
|
||
//
|
||
|
||
BufferSize -= sizeof (DWORD);
|
||
RtlZeroMemory (IndexByByte (Buffer, BufferSize), sizeof (DWORD));
|
||
|
||
TriageDump->ValidOffset = ( TriageDump->SizeOfDump - sizeof (ULONG) );
|
||
TriageDump->ContextOffset = FIELD_OFFSET (DUMP_HEADER, ContextRecord);
|
||
TriageDump->ExceptionOffset = FIELD_OFFSET (DUMP_HEADER, Exception);
|
||
TriageDump->BrokenDriverOffset = 0;
|
||
TriageDump->ServicePackBuild = ServicePackBuild;
|
||
TriageDump->TriageOptions = TriageOptions;
|
||
|
||
Offset = ALIGN_8 (sizeof(DUMP_HEADER) + sizeof (TRIAGE_DUMP));
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the Mm Offset, if necessary.
|
||
//
|
||
|
||
SizeOfSection = ALIGN_8 (MmSizeOfTriageInformation());
|
||
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->MmOffset = Offset;
|
||
Offset += SizeOfSection;
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the Unloaded Drivers Offset, if necessary.
|
||
//
|
||
|
||
SizeOfSection = ALIGN_8 (MmSizeOfUnloadedDriverInformation());
|
||
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->UnloadedDriversOffset = Offset;
|
||
Offset += SizeOfSection;
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the Prcb Offset, if necessary.
|
||
//
|
||
|
||
if (Fields & TRIAGE_DUMP_PRCB) {
|
||
SizeOfSection = ALIGN_8 (sizeof (KPRCB));
|
||
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->PrcbOffset = Offset;
|
||
Offset += SizeOfSection;
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the Process Offset, if necessary.
|
||
//
|
||
|
||
if (Fields & TRIAGE_DUMP_PROCESS) {
|
||
SizeOfSection = ALIGN_8 (sizeof (EPROCESS));
|
||
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->ProcessOffset = Offset;
|
||
Offset += SizeOfSection;
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the Thread Offset, if necessary.
|
||
//
|
||
|
||
if (Fields & TRIAGE_DUMP_THREAD) {
|
||
SizeOfSection = ALIGN_8 (sizeof (ETHREAD));
|
||
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->ThreadOffset = Offset;
|
||
Offset += SizeOfSection;
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the CallStack Offset, if necessary.
|
||
//
|
||
|
||
if (Fields & TRIAGE_DUMP_STACK) {
|
||
|
||
//
|
||
// If there is a stack, calculate its size.
|
||
//
|
||
|
||
//
|
||
// Remember: the callstack grows downward in memory, therefore,
|
||
// Base >= Current = SP = Top > Limit.
|
||
//
|
||
|
||
if (Thread->KernelStackResident) {
|
||
|
||
ULONG_PTR StackBase;
|
||
ULONG_PTR StackLimit;
|
||
ULONG_PTR StackTop;
|
||
|
||
StackBase = (ULONG_PTR) Thread->StackBase;
|
||
StackLimit = (ULONG_PTR) Thread->StackLimit;
|
||
|
||
//
|
||
// Don't necessarily trust that SP is valid. If it's
|
||
// outside the reasonable range, just copy from the limit.
|
||
//
|
||
|
||
if (StackLimit < STACK_POINTER (Context) &&
|
||
STACK_POINTER (Context) <= StackBase) {
|
||
|
||
StackTop = STACK_POINTER (Context);
|
||
} else {
|
||
StackTop = (ULONG_PTR) Thread->StackLimit;
|
||
}
|
||
|
||
ASSERT (StackLimit <= StackTop && StackTop < StackBase);
|
||
|
||
//
|
||
// There is a valid stack. Note that we limit the size of
|
||
// the triage dump stack to MAX_TRIAGE_STACK_SIZE (currently
|
||
// 16 KB).
|
||
//
|
||
|
||
SizeOfSection = (ULONG) min (StackBase - StackTop,
|
||
MAX_TRIAGE_STACK_SIZE - 1);
|
||
|
||
if (SizeOfSection) {
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->CallStackOffset = Offset;
|
||
TriageDump->SizeOfCallStack = SizeOfSection;
|
||
TriageDump->TopOfStack = StackTop;
|
||
Offset += SizeOfSection;
|
||
Offset = ALIGN_8 (Offset);
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// There is not a valid stack.
|
||
//
|
||
}
|
||
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
#if defined (_IA64_)
|
||
|
||
//
|
||
// The IA64 contains two callstacks. The first is the normal
|
||
// callstack, and the second is a scratch region where
|
||
// the processor can spill registers. It is this latter stack,
|
||
// the backing-store, that we now save.
|
||
//
|
||
|
||
if ( Fields & TRIAGE_DUMP_STACK ) {
|
||
|
||
ULONG_PTR BStoreBase;
|
||
ULONG_PTR BStoreLimit;
|
||
|
||
BStoreBase = (ULONG_PTR) Thread->InitialBStore;
|
||
BStoreLimit = (ULONG_PTR) Thread->BStoreLimit;
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: IA64 BStore: base %p limit %p\n",
|
||
BStoreBase,
|
||
BStoreLimit));
|
||
|
||
SizeOfSection = (ULONG) (BStoreLimit - BStoreBase);
|
||
|
||
//
|
||
// The calculated size had better be less than the maximum size
|
||
// for a BSTORE region.
|
||
//
|
||
|
||
ASSERT ( SizeOfSection < KERNEL_LARGE_BSTORE_SIZE );
|
||
|
||
if (SizeOfSection) {
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->ArchitectureSpecific.Ia64.BStoreOffset = Offset;
|
||
TriageDump->ArchitectureSpecific.Ia64.SizeOfBStore = SizeOfSection;
|
||
TriageDump->ArchitectureSpecific.Ia64.LimitOfBStore= BStoreLimit;
|
||
Offset += SizeOfSection;
|
||
Offset = ALIGN_8 (Offset);
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
if (Fields & TRIAGE_DUMP_DEBUGGER_DATA) {
|
||
if (Offset + ALIGN_8(sizeof(KdDebuggerDataBlock)) < BufferSize) {
|
||
TriageDump->DebuggerDataOffset = Offset;
|
||
TriageDump->DebuggerDataSize = sizeof(KdDebuggerDataBlock);
|
||
Offset += ALIGN_8(sizeof(KdDebuggerDataBlock));
|
||
Offset = ALIGN_8 (Offset);
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
//
|
||
// Set the Driver List Offset, if necessary.
|
||
//
|
||
|
||
Status = IopGetLoadedDriverInfo (&DriverCount, &SizeOfStringData);
|
||
|
||
if (NT_SUCCESS (Status) && (Fields & TRIAGE_DUMP_DRIVER_LIST)) {
|
||
SizeOfSection = ALIGN_8 (DriverCount * sizeof (DUMP_DRIVER_ENTRY));
|
||
|
||
if (SizeOfSection) {
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->DriverListOffset = Offset;
|
||
TriageDump->DriverCount = DriverCount;
|
||
Offset += SizeOfSection;
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
SizeOfSection = 0;
|
||
SizeOfStringData = 0;
|
||
}
|
||
|
||
//
|
||
// Set the String Pool offset.
|
||
//
|
||
|
||
SizeOfSection = ALIGN_8 (SizeOfStringData +
|
||
DriverCount * (sizeof (WCHAR) + sizeof (DUMP_STRING)));
|
||
|
||
if (SizeOfSection) {
|
||
if (Offset + SizeOfSection < BufferSize) {
|
||
TriageDump->StringPoolOffset = (ULONG)Offset;
|
||
TriageDump->StringPoolSize = SizeOfSection;
|
||
Offset += SizeOfSection;
|
||
Offset = ALIGN_8 (Offset);
|
||
} else {
|
||
TriageDump->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
if (Fields & TRIAGE_DUMP_DATA_BLOCKS) {
|
||
|
||
#ifdef _IA64_
|
||
volatile KPCR* const Pcr = KeGetPcr();
|
||
|
||
//
|
||
// In certain failures there is a switch from
|
||
// the current thread's stack and store to
|
||
// a special stack and store. The PCR contains
|
||
// stack and store pointers which will be different
|
||
// from the current thread's stack and store pointers
|
||
// so save the extra stack and store if they are.
|
||
//
|
||
|
||
if ((PVOID)Pcr->InitialBStore != Thread->InitialBStore ||
|
||
(PVOID)Pcr->BStoreLimit != Thread->BStoreLimit) {
|
||
ULONG64 StoreTop, StoreBase;
|
||
ULONG FrameSize;
|
||
ULONG StoreSize;
|
||
|
||
StoreTop = Context->RsBSP;
|
||
StoreBase = Pcr->InitialBStore;
|
||
FrameSize = (ULONG)(Context->StIFS & PFS_SIZE_MASK);
|
||
|
||
// Add in a ULONG64 for every register in the
|
||
// current frame. While doing so, check for
|
||
// spill entries.
|
||
while (FrameSize-- > 0) {
|
||
StoreTop += sizeof(ULONG64);
|
||
if ((StoreTop & 0x1f8) == 0x1f8) {
|
||
// Spill will be placed at this address so
|
||
// account for it.
|
||
StoreTop += sizeof(ULONG64);
|
||
}
|
||
}
|
||
|
||
if (StoreTop < Pcr->InitialBStore ||
|
||
StoreTop >= Pcr->BStoreLimit) {
|
||
// BSP isn't in the PCR store range so
|
||
// just save the whole thing.
|
||
StoreTop = Pcr->BStoreLimit;
|
||
}
|
||
|
||
StoreSize = (ULONG)(StoreTop - Pcr->InitialBStore);
|
||
if (StoreSize > MAX_TRIAGE_STACK_SIZE) {
|
||
StoreSize = MAX_TRIAGE_STACK_SIZE;
|
||
StoreBase = StoreTop - StoreSize;
|
||
}
|
||
|
||
IoAddTriageDumpDataBlock((PVOID)StoreBase, StoreSize);
|
||
}
|
||
|
||
if ((PVOID)Pcr->InitialStack != Thread->InitialStack ||
|
||
(PVOID)Pcr->StackLimit != Thread->StackLimit) {
|
||
ULONG64 StackTop;
|
||
ULONG StackSize;
|
||
|
||
StackTop = STACK_POINTER(Context);
|
||
if (StackTop < Pcr->StackLimit ||
|
||
StackTop >= Pcr->InitialStack) {
|
||
// SP isn't in the PCR stack range so
|
||
// just save the whole thing.
|
||
StackTop = Pcr->StackLimit;
|
||
}
|
||
|
||
StackSize = (ULONG)(Pcr->InitialStack - StackTop);
|
||
if (StackSize > MAX_TRIAGE_STACK_SIZE) {
|
||
StackSize = MAX_TRIAGE_STACK_SIZE;
|
||
}
|
||
|
||
IoAddTriageDumpDataBlock((PVOID)StackTop, StackSize);
|
||
}
|
||
#endif
|
||
|
||
// Add data blocks which might be referred to by
|
||
// the context or other runtime state.
|
||
IopAddRunTimeTriageDataBlocks(Context,
|
||
(PVOID*)TriageDump->TopOfStack,
|
||
(PVOID*)((PUCHAR)TriageDump->TopOfStack +
|
||
TriageDump->SizeOfCallStack),
|
||
#ifdef _IA64_
|
||
(PVOID*)Thread->InitialBStore,
|
||
(PVOID*)((PUCHAR)Thread->InitialBStore +
|
||
TriageDump->ArchitectureSpecific.Ia64.SizeOfBStore)
|
||
#else
|
||
NULL, NULL
|
||
#endif
|
||
);
|
||
|
||
// Check which data blocks fit.
|
||
Offset = IopSizeTriageDumpDataBlocks(TriageDump, Offset, BufferSize);
|
||
Offset = ALIGN_8 (Offset);
|
||
}
|
||
|
||
ASSERT_ALIGNMENT (Offset, 8);
|
||
|
||
BytesToWrite = (ULONG)Offset;
|
||
BufferAddress = ((ULONG_PTR) Buffer) - sizeof(DUMP_HEADER);
|
||
|
||
//
|
||
// Write the Mm information.
|
||
//
|
||
|
||
if (TriageDump->MmOffset) {
|
||
|
||
Address = (LPVOID) (BufferAddress + TriageDump->MmOffset);
|
||
MmWriteTriageInformation (Address);
|
||
}
|
||
|
||
if (TriageDump->UnloadedDriversOffset) {
|
||
|
||
Address = (LPVOID) (BufferAddress + TriageDump->UnloadedDriversOffset);
|
||
MmWriteUnloadedDriverInformation (Address);
|
||
}
|
||
|
||
//
|
||
// Write the PRCB.
|
||
//
|
||
|
||
if (TriageDump->PrcbOffset) {
|
||
|
||
Address = (LPVOID) (BufferAddress + TriageDump->PrcbOffset);
|
||
RtlCopyMemory (Address,
|
||
KeGetCurrentPrcb (),
|
||
sizeof (KPRCB)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Write the EPROCESS.
|
||
//
|
||
|
||
if (TriageDump->ProcessOffset) {
|
||
|
||
Address = (LPVOID) (BufferAddress + TriageDump->ProcessOffset);
|
||
RtlCopyMemory (Address,
|
||
Thread->ApcState.Process,
|
||
sizeof (EPROCESS)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Write the ETHREAD.
|
||
//
|
||
|
||
if (TriageDump->ThreadOffset) {
|
||
|
||
Address = (LPVOID) (BufferAddress + TriageDump->ThreadOffset);
|
||
RtlCopyMemory (Address,
|
||
Thread,
|
||
sizeof (ETHREAD));
|
||
}
|
||
|
||
//
|
||
// Write the Call Stack.
|
||
//
|
||
|
||
if (TriageDump->CallStackOffset) {
|
||
|
||
PVOID StackTop;
|
||
|
||
ASSERT (TriageDump->SizeOfCallStack != 0);
|
||
|
||
StackTop = (PVOID)TriageDump->TopOfStack;
|
||
|
||
ASSERT (IopIsAddressRangeValid (StackTop, TriageDump->SizeOfCallStack));
|
||
Address = (LPVOID) (BufferAddress + TriageDump->CallStackOffset);
|
||
RtlCopyMemory (Address,
|
||
StackTop,
|
||
TriageDump->SizeOfCallStack
|
||
);
|
||
}
|
||
|
||
#if defined (_IA64_)
|
||
|
||
//
|
||
// Write the IA64 BStore.
|
||
//
|
||
|
||
if ( TriageDump->ArchitectureSpecific.Ia64.BStoreOffset ) {
|
||
|
||
ASSERT (IopIsAddressRangeValid (Thread->InitialBStore,
|
||
TriageDump->ArchitectureSpecific.Ia64.SizeOfBStore));
|
||
Address = (PVOID) (BufferAddress + TriageDump->ArchitectureSpecific.Ia64.BStoreOffset);
|
||
RtlCopyMemory (Address,
|
||
Thread->InitialBStore,
|
||
TriageDump->ArchitectureSpecific.Ia64.SizeOfBStore
|
||
);
|
||
}
|
||
|
||
#endif // IA64
|
||
|
||
//
|
||
// Write the debugger data block.
|
||
//
|
||
|
||
if (TriageDump->DebuggerDataOffset) {
|
||
Address = (LPVOID) (BufferAddress + TriageDump->DebuggerDataOffset);
|
||
|
||
RtlCopyMemory (Address,
|
||
&KdDebuggerDataBlock,
|
||
sizeof(KdDebuggerDataBlock)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Write the Driver List.
|
||
//
|
||
|
||
if (TriageDump->DriverListOffset &&
|
||
TriageDump->StringPoolOffset) {
|
||
|
||
Status = IopWriteDriverList (BufferAddress,
|
||
BufferSize,
|
||
TriageDump->DriverListOffset,
|
||
TriageDump->StringPoolOffset
|
||
);
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
TriageDump->DriverListOffset = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Write the data blocks.
|
||
//
|
||
|
||
IopWriteTriageDumpDataBlocks(TriageDump, (PUCHAR)BufferAddress);
|
||
|
||
|
||
ASSERT (BytesToWrite < BufferSize);
|
||
ASSERT (ALIGN_UP (BytesToWrite, PAGE_SIZE) < BufferSize);
|
||
|
||
//
|
||
// Write the valid status to the end of the dump.
|
||
//
|
||
|
||
*((ULONG *)IndexByByte (Buffer, BufferSize)) = TRIAGE_DUMP_VALID ;
|
||
|
||
//
|
||
// Re-adjust the buffer size.
|
||
//
|
||
|
||
BufferSize += sizeof (DWORD);
|
||
|
||
//
|
||
// NOTE: This routine writes the entire buffer, even if it is not
|
||
// all required.
|
||
//
|
||
|
||
Status = IopWriteToDisk (Buffer,
|
||
BufferSize,
|
||
DriverWriteRoutine,
|
||
Mcb,
|
||
Mdl,
|
||
DriverTransferSize,
|
||
KbDumpIoBody
|
||
);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IopWritePageToDisk(
|
||
IN PDUMP_DRIVER_WRITE DriverWrite,
|
||
IN OUT PLARGE_INTEGER * McbBuffer,
|
||
IN OUT ULONG DriverTransferSize,
|
||
IN PFN_NUMBER PageFrameIndex
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write the page described by PageFrameIndex to the disk/file (DriverWrite,
|
||
McbBuffer) and update the MCB buffer to reflect the new position in the
|
||
file.
|
||
|
||
Arguments:
|
||
|
||
DriverWrite - The driver write routine.
|
||
|
||
McbBuffer - A pointer to the MCB array. This array is terminated by
|
||
a zero-length MCB entry. On success, this pointer is updated
|
||
to reflect the new position in the MCB array.
|
||
|
||
NB: MCB[0] is the size and MCB[1] is the offset.
|
||
|
||
DriverTransferSize - The maximum transfer size for this driver.
|
||
|
||
PageFrameIndex - The page to be written.
|
||
|
||
Return Values:
|
||
|
||
NTSTATUS code.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PFN_NUMBER MdlHack [ (sizeof (MDL) / sizeof (PFN_NUMBER)) + 1];
|
||
PPFN_NUMBER PfnArray;
|
||
PLARGE_INTEGER Mcb;
|
||
ULONG ByteCount;
|
||
ULONG ByteOffset;
|
||
ULONG BytesToWrite;
|
||
PMDL TempMdl;
|
||
|
||
|
||
ASSERT ( DriverWrite );
|
||
ASSERT ( McbBuffer );
|
||
ASSERT ( DriverTransferSize && DriverTransferSize >= PAGE_SIZE );
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
//
|
||
// Initialization
|
||
//
|
||
|
||
TempMdl = (PMDL) &MdlHack[0];
|
||
Mcb = *McbBuffer;
|
||
BytesToWrite = PAGE_SIZE;
|
||
|
||
|
||
//
|
||
// Initialze the MDL to point to this page.
|
||
//
|
||
|
||
MmInitializeMdl (TempMdl, NULL, PAGE_SIZE);
|
||
|
||
// TempMdl->StartVa = (PVOID) (PageFrameIndex << PAGE_SHIFT);
|
||
PfnArray = MmGetMdlPfnArray ( TempMdl );
|
||
PfnArray[0] = PageFrameIndex;
|
||
|
||
//
|
||
// We loop for the cases when the space remaining in this block (Mcb [0])
|
||
// is less than one page. Generally the Mcb will be large enough to hold
|
||
// the entire page and this loop will only be executed once. When Mcb[0]
|
||
// is less than a page, we will write the first part of the page to this
|
||
// Mcb then increment the Mcb and write the remaining part to the next
|
||
// page.
|
||
//
|
||
|
||
ByteOffset = 0;
|
||
|
||
while ( BytesToWrite ) {
|
||
|
||
ASSERT ( Mcb[0].QuadPart != 0 );
|
||
|
||
ByteCount = (ULONG) min3 ((LONGLONG) BytesToWrite,
|
||
(LONGLONG) DriverTransferSize,
|
||
Mcb[0].QuadPart
|
||
);
|
||
|
||
|
||
ASSERT ( ByteCount != 0 );
|
||
|
||
//
|
||
// Update the MDL byte count and byte offset.
|
||
//
|
||
|
||
TempMdl->ByteCount = ByteCount;
|
||
TempMdl->ByteOffset = ByteOffset;
|
||
|
||
//
|
||
// Map the MDL. The flags are updated to show that MappedSystemVa
|
||
// is valid (which should probably be done in MmMapMemoryDumpMdl).
|
||
//
|
||
|
||
MmMapMemoryDumpMdl ( TempMdl );
|
||
TempMdl->MdlFlags |= ( MDL_PAGES_LOCKED | MDL_MAPPED_TO_SYSTEM_VA );
|
||
TempMdl->StartVa = PAGE_ALIGN (TempMdl->MappedSystemVa);
|
||
|
||
IopInvokeDumpIoCallbacks((PUCHAR)TempMdl->StartVa + ByteOffset,
|
||
ByteCount, KbDumpIoBody);
|
||
|
||
//
|
||
// Write the bufffer.
|
||
//
|
||
|
||
Status = DriverWrite ( &Mcb[1], TempMdl );
|
||
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
BytesToWrite -= ByteCount;
|
||
ByteOffset += ByteCount;
|
||
|
||
Mcb[0].QuadPart -= ByteCount;
|
||
Mcb[1].QuadPart += ByteCount;
|
||
|
||
//
|
||
// If there is no more room for this MCB, go to the next one.
|
||
//
|
||
|
||
if ( Mcb[0].QuadPart == 0 ) {
|
||
|
||
Mcb += 2;
|
||
|
||
//
|
||
// We have filled up all the space in the paging file.
|
||
//
|
||
|
||
if ( Mcb[0].QuadPart == 0) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Pagefile is full.\n"));
|
||
return STATUS_END_OF_FILE;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
*McbBuffer = Mcb;
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IopWriteSummaryDump(
|
||
IN PRTL_BITMAP PageMap,
|
||
IN PDUMP_DRIVER_WRITE DriverWriteRoutine,
|
||
IN PANSI_STRING ProgressMessage,
|
||
IN PUCHAR MessageBuffer,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT ULONG DriverTransferSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write a summary dump to the disk.
|
||
|
||
Arguments:
|
||
|
||
|
||
PageMap - A bitmap of the pages that need to be written.
|
||
|
||
DriverWriteRoutine - The driver's write routine.
|
||
|
||
ProgressMessage - The "Percent Complete" message.
|
||
|
||
MessageBuffer - Not used. Must be NULL.
|
||
|
||
Mcb - Message Control Block where the data is to be written.
|
||
|
||
DriverTransferSize - The maximum transfer size for the driver.
|
||
|
||
Return Values:
|
||
|
||
NTSTATUS code.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFN_NUMBER PageFrameIndex;
|
||
NTSTATUS Status;
|
||
|
||
ULONG WriteCount;
|
||
ULONG MaxWriteCount;
|
||
ULONG Step;
|
||
|
||
#if !DBG
|
||
UNREFERENCED_PARAMETER (MessageBuffer);
|
||
#endif
|
||
|
||
ASSERT ( DriverWriteRoutine != NULL );
|
||
ASSERT ( Mcb != NULL );
|
||
ASSERT ( DriverTransferSize != 0 );
|
||
ASSERT ( MessageBuffer == NULL );
|
||
|
||
|
||
MaxWriteCount = RtlNumberOfSetBits ( PageMap );
|
||
ASSERT (MaxWriteCount != 0);
|
||
Step = MaxWriteCount / 100;
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Summary Dump\n"
|
||
" Writing %x pages to disk from a total of %x\n",
|
||
MaxWriteCount,
|
||
PageMap->SizeOfBitMap
|
||
));
|
||
//
|
||
// Loop over all pages in the system and write those that are set
|
||
// in the bitmap.
|
||
//
|
||
|
||
WriteCount = 0;
|
||
for ( PageFrameIndex = 0;
|
||
PageFrameIndex < PageMap->SizeOfBitMap;
|
||
PageFrameIndex++) {
|
||
|
||
|
||
//
|
||
// If this page needs to be included in the dump file.
|
||
//
|
||
|
||
if ( RtlCheckBit (PageMap, PageFrameIndex) ) {
|
||
|
||
if (++WriteCount % Step == 0) {
|
||
|
||
//
|
||
// Update the progress percentage.
|
||
//
|
||
|
||
IopDisplayString ("%Z: %3d\r",
|
||
ProgressMessage,
|
||
(WriteCount * 100) / MaxWriteCount
|
||
);
|
||
}
|
||
|
||
ASSERT ( WriteCount <= MaxWriteCount );
|
||
|
||
//
|
||
// Write the page to disk.
|
||
//
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
Status = IopWritePageToDisk (
|
||
DriverWriteRoutine,
|
||
Mcb,
|
||
DriverTransferSize,
|
||
PageFrameIndex
|
||
);
|
||
|
||
if (!NT_SUCCESS (Status)) {
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IopInitializeSummaryDump(
|
||
IN OUT PMEMORY_DUMP MemoryDump,
|
||
IN PDUMP_CONTROL_BLOCK DumpControlBlock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a summary dump header. In particular it initializes
|
||
a bitmap that contains a map of kernel memory.
|
||
|
||
Arguments:
|
||
|
||
MemoryDump - The memory dump.
|
||
|
||
DumpControlBlock - A pointer to the dump control block.
|
||
|
||
Return Value:
|
||
|
||
Non-NULL - A pointer to the summary dump header
|
||
|
||
NULL - Error
|
||
|
||
--*/
|
||
{
|
||
ULONG ActualPages;
|
||
|
||
ASSERT ( MemoryDump != NULL );
|
||
ASSERT ( DumpControlBlock != NULL );
|
||
|
||
//
|
||
// Fill the header with signatures.
|
||
//
|
||
|
||
RtlFillMemoryUlong( &MemoryDump->Summary,
|
||
sizeof (SUMMARY_DUMP),
|
||
DUMP_SUMMARY_SIGNATURE);
|
||
|
||
//
|
||
// Set the size and valid signature.
|
||
//
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Review for Win64
|
||
//
|
||
// Computing the bitmap size is probably wrong for 64 bit.
|
||
//
|
||
|
||
MemoryDump->Summary.BitmapSize =
|
||
(ULONG)( MmPhysicalMemoryBlock->Run[MmPhysicalMemoryBlock->NumberOfRuns-1].BasePage +
|
||
MmPhysicalMemoryBlock->Run[MmPhysicalMemoryBlock->NumberOfRuns-1].PageCount );
|
||
|
||
MemoryDump->Summary.ValidDump = DUMP_SUMMARY_VALID;
|
||
|
||
//
|
||
// Construct the kernel memory bitmap.
|
||
//
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Review for Win64
|
||
//
|
||
// Actual will probably need to be a 64-bit value for Win64.
|
||
//
|
||
|
||
ActualPages = IopCreateSummaryDump (MemoryDump);
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID, CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Kernel Pages = %x\n",
|
||
ActualPages ));
|
||
|
||
if (ActualPages == 0) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Set the actual number of physical pages in the summary dump
|
||
//
|
||
|
||
MemoryDump->Summary.Pages = ActualPages;
|
||
MemoryDump->Summary.HeaderSize = DumpControlBlock->HeaderSize;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IopWriteSummaryHeader(
|
||
IN PSUMMARY_DUMP SummaryHeader,
|
||
IN PDUMP_DRIVER_WRITE WriteRoutine,
|
||
IN OUT PLARGE_INTEGER * McbBuffer,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG WriteSize,
|
||
IN ULONG Length
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write the summary dump header to the dump file.
|
||
|
||
Arguments:
|
||
|
||
SummaryHeader - pointer to the summary dump bitmap
|
||
|
||
WriteRoutine - dump driver write function
|
||
|
||
McbBuffer - Pointer to the dump file MCB array.
|
||
|
||
Mdl - Pointer to an MDL
|
||
|
||
WriteSize - the max transfer size for the dump driver
|
||
|
||
Length - the length of this transfer
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG BytesRemaining;
|
||
ULONG_PTR MemoryAddress;
|
||
ULONG ByteOffset;
|
||
ULONG ByteCount;
|
||
PLARGE_INTEGER Mcb;
|
||
|
||
Mcb = *McbBuffer;
|
||
|
||
BytesRemaining = Length;
|
||
MemoryAddress = (ULONG_PTR) SummaryHeader;
|
||
|
||
IopInvokeDumpIoCallbacks(SummaryHeader, Length, KbDumpIoBody);
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Writing SUMMARY dump header to disk\n" ));
|
||
|
||
|
||
while ( BytesRemaining ) {
|
||
|
||
ByteOffset = BYTE_OFFSET ( MemoryAddress );
|
||
|
||
//
|
||
// See if the number of bytes to write is greator than the crash
|
||
// drives max transfer.
|
||
//
|
||
|
||
if ( BytesRemaining <= WriteSize) {
|
||
ByteCount = BytesRemaining;
|
||
} else {
|
||
ByteCount = WriteSize;
|
||
}
|
||
|
||
//
|
||
// If the byteCount is greater than the remaining mcb then correct it.
|
||
//
|
||
|
||
if ( ByteCount > Mcb[0].QuadPart) {
|
||
ByteCount = Mcb[0].LowPart;
|
||
}
|
||
|
||
Mdl->ByteCount = ByteCount;
|
||
Mdl->ByteOffset = ByteOffset;
|
||
Mdl->MappedSystemVa = (PVOID) MemoryAddress;
|
||
|
||
//
|
||
// Get the actual physical frame and create an mdl.
|
||
//
|
||
|
||
IopMapVirtualToPhysicalMdl ( Mdl, MemoryAddress, ByteCount );
|
||
|
||
//
|
||
// Write to disk.
|
||
//
|
||
|
||
Status = WriteRoutine ( &Mcb[1], Mdl );
|
||
|
||
if ( !NT_SUCCESS (Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Adjust bytes remaining.
|
||
//
|
||
|
||
BytesRemaining -= ByteCount;
|
||
MemoryAddress += ByteCount;
|
||
|
||
Mcb[0].QuadPart = Mcb[0].QuadPart - ByteCount;
|
||
Mcb[1].QuadPart = Mcb[1].QuadPart + ByteCount;
|
||
|
||
if (Mcb[0].QuadPart == 0) {
|
||
Mcb += 2;
|
||
}
|
||
|
||
if (Mcb[0].QuadPart == 0) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Pagefile is full.\n"));
|
||
return STATUS_END_OF_FILE;
|
||
}
|
||
}
|
||
|
||
*McbBuffer = Mcb;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IopWriteToDisk(
|
||
IN PVOID Buffer,
|
||
IN ULONG WriteLength,
|
||
IN PDUMP_DRIVER_WRITE DriverWriteRoutine,
|
||
IN OUT PLARGE_INTEGER * McbBuffer,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG DriverTransferSize,
|
||
IN KBUGCHECK_DUMP_IO_TYPE DataType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write the summary dump header to the dump file.
|
||
|
||
Arguments:
|
||
|
||
Buffer - Pointer to the buffer to write.
|
||
|
||
WriteLength - The length of this transfer.
|
||
|
||
DriverWriteRoutine - Dump driver write function.
|
||
|
||
McbBuffer - Pointer to the dump file Mapped Control Block.
|
||
|
||
Mdl - Pointer to an MDL.
|
||
|
||
DriverTransferSize - The max transfer size for the dump driver.
|
||
|
||
DataType - Type of data being written, for I/O callbacks.
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
ULONG BytesRemaining;
|
||
ULONG_PTR MemoryAddress;
|
||
ULONG ByteOffset;
|
||
ULONG ByteCount;
|
||
PLARGE_INTEGER Mcb;
|
||
|
||
ASSERT (Buffer);
|
||
ASSERT (WriteLength);
|
||
ASSERT (DriverWriteRoutine);
|
||
ASSERT (McbBuffer && *McbBuffer);
|
||
ASSERT (Mdl);
|
||
ASSERT (DriverTransferSize >= IO_DUMP_MINIMUM_TRANSFER_SIZE &&
|
||
DriverTransferSize <= IO_DUMP_MAXIMUM_TRANSFER_SIZE);
|
||
|
||
|
||
IopInvokeDumpIoCallbacks(Buffer, WriteLength, DataType);
|
||
|
||
Mcb = *McbBuffer;
|
||
BytesRemaining = WriteLength;
|
||
MemoryAddress = (ULONG_PTR) Buffer;
|
||
|
||
while ( BytesRemaining ) {
|
||
|
||
ASSERT (IopDumpControlBlock->FileDescriptorArray <= Mcb &&
|
||
(LPBYTE) Mcb < (LPBYTE) IopDumpControlBlock->FileDescriptorArray +
|
||
IopDumpControlBlock->FileDescriptorSize
|
||
);
|
||
|
||
if (Mcb[0].QuadPart == 0) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Pagefile is full.\n"));
|
||
return STATUS_END_OF_FILE;
|
||
}
|
||
|
||
ByteOffset = BYTE_OFFSET ( MemoryAddress );
|
||
|
||
//
|
||
// See if the number of bytes to write is greator than the crash
|
||
// drives max transfer.
|
||
//
|
||
|
||
ByteCount = min ( BytesRemaining, DriverTransferSize );
|
||
|
||
//
|
||
// If the byteCount is greater than the remaining mcb then correct it.
|
||
//
|
||
|
||
if (ByteCount > Mcb[0].QuadPart) {
|
||
ByteCount = Mcb[0].LowPart;
|
||
}
|
||
|
||
Mdl->ByteCount = ByteCount;
|
||
Mdl->ByteOffset = ByteOffset;
|
||
Mdl->MappedSystemVa = (PVOID) MemoryAddress;
|
||
|
||
//
|
||
// Get the actual physical frame and create an mdl.
|
||
//
|
||
|
||
IopMapVirtualToPhysicalMdl(Mdl, MemoryAddress, ByteCount);
|
||
|
||
KdCheckForDebugBreak();
|
||
|
||
if (!NT_SUCCESS( DriverWriteRoutine ( &Mcb[1], Mdl ) )) {
|
||
|
||
//
|
||
// We are in deep trouble if we failed the write.
|
||
//
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Failed to write Mcb = %p, Mdl = %p to disk\n",
|
||
&Mcb[1],
|
||
Mdl
|
||
));
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Adjust bytes remaining.
|
||
//
|
||
|
||
ASSERT ( BytesRemaining >= ByteCount );
|
||
ASSERT ( ByteCount != 0 );
|
||
|
||
BytesRemaining -= ByteCount;
|
||
MemoryAddress += ByteCount;
|
||
|
||
Mcb[0].QuadPart -= ByteCount;
|
||
Mcb[1].QuadPart += ByteCount;
|
||
|
||
if (Mcb[0].QuadPart == 0) {
|
||
Mcb += 2;
|
||
}
|
||
}
|
||
|
||
*McbBuffer = Mcb;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
IopMapVirtualToPhysicalMdl(
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG_PTR MemoryAddress,
|
||
IN ULONG Length
|
||
)
|
||
{
|
||
ULONG Pages;
|
||
PPFN_NUMBER Pfn;
|
||
PCHAR BaseVa;
|
||
PHYSICAL_ADDRESS PhysicalAddress;
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Review for Win64
|
||
//
|
||
// This whole function needs to be revisited for Win64.
|
||
// There are a ton of tacit assumptions here about the
|
||
// size of a PFN.
|
||
//
|
||
|
||
//
|
||
// Begin by determining the base physical page of the start of the address
|
||
// range and filling in the MDL appropriately.
|
||
//
|
||
|
||
Mdl->StartVa = PAGE_ALIGN ( MemoryAddress );
|
||
Mdl->ByteOffset = BYTE_OFFSET ( MemoryAddress );
|
||
Mdl->ByteCount = Length;
|
||
Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
|
||
|
||
//
|
||
// Compute the number of pages spanned
|
||
//
|
||
|
||
Pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( MemoryAddress, Length );
|
||
Pfn = MmGetMdlPfnArray ( Mdl );
|
||
|
||
//
|
||
// Map all of the pages for this transfer until there are no more remaining
|
||
// to be mapped.
|
||
//
|
||
|
||
BaseVa = PAGE_ALIGN ( MemoryAddress );
|
||
|
||
while ( Pages ) {
|
||
PhysicalAddress = MmGetPhysicalAddress ( BaseVa );
|
||
*Pfn++ = (PFN_NUMBER)(PhysicalAddress.QuadPart >> PAGE_SHIFT);
|
||
BaseVa += PAGE_SIZE;
|
||
Pages--;
|
||
}
|
||
|
||
//
|
||
// All of the PFNs for the address range have been filled in so map the
|
||
// physical memory into virtual address space using crash dump PTE.
|
||
//
|
||
|
||
// MmMapMemoryDumpMdl( pMdl );
|
||
}
|
||
|
||
|
||
|
||
ULONG
|
||
IopCreateSummaryDump (
|
||
IN PMEMORY_DUMP MemoryDump
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines the kernel memory and data structures to include
|
||
in the summary memory dump.
|
||
|
||
NOTE: This function uses MmGetPhysicalAddress. MmGetPhysicalAddress does
|
||
not acquire any locks. It uses a set of macros for translation.
|
||
|
||
|
||
Arguments:
|
||
|
||
MemoryDump - The memory dump.
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PRTL_BITMAP BitMap;
|
||
ULONG UserPages;
|
||
LARGE_INTEGER DumpFileSize;
|
||
ULONG PagesUsed;
|
||
ULONG PagesInDumpFile;
|
||
PSUMMARY_DUMP Summary;
|
||
MM_KERNEL_DUMP_CONTEXT Context;
|
||
|
||
//
|
||
// Validation
|
||
//
|
||
|
||
ASSERT (MemoryDump != NULL);
|
||
|
||
//
|
||
// Initialize Bit Map, set the size and buffer address.
|
||
//
|
||
|
||
Summary = &MemoryDump->Summary;
|
||
BitMap = (PRTL_BITMAP) &Summary->Bitmap;
|
||
BitMap->SizeOfBitMap = Summary->BitmapSize; // Why??
|
||
BitMap->Buffer = Summary->Bitmap.Buffer;
|
||
|
||
//
|
||
// Clear all bits
|
||
//
|
||
|
||
RtlClearAllBits (BitMap);
|
||
|
||
//
|
||
// Have MM initialize the kernel memory to dump
|
||
//
|
||
|
||
Context.Context = Summary;
|
||
Context.SetDumpRange = IoSetDumpRange;
|
||
Context.FreeDumpRange = IoFreeDumpRange;
|
||
|
||
MmGetKernelDumpRange (&Context);
|
||
|
||
PagesUsed = RtlNumberOfSetBits ( BitMap );
|
||
|
||
//
|
||
// See If we have room to Include user va for the current process
|
||
//
|
||
|
||
DumpFileSize = MemoryDump->Header.RequiredDumpSpace;
|
||
DumpFileSize.QuadPart -= IopDumpControlBlock->HeaderSize;
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Win64
|
||
//
|
||
// For Win64 the total number of physical pages can exceed 2^32.
|
||
//
|
||
|
||
PagesInDumpFile = (ULONG)(PFN_NUMBER)(DumpFileSize.QuadPart >> PAGE_SHIFT);
|
||
|
||
//
|
||
// Only copy user virtual if there extra room in the dump file.
|
||
//
|
||
|
||
UserPages = 0;
|
||
|
||
PagesUsed += UserPages;
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Number of user mode pages for kernel dump = %x\n",
|
||
UserPages
|
||
));
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
"CRASHDUMP: Kernel Dump, Header = %p\n",
|
||
Summary
|
||
));
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_TRACE,
|
||
" BitMapSize: %x\n"
|
||
" Pages: %x\n"
|
||
" BitMapBuffer: %p\n",
|
||
Summary->BitmapSize,
|
||
PagesUsed,
|
||
BitMap->Buffer
|
||
));
|
||
|
||
return PagesUsed;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
IopDeleteNonExistentMemory(
|
||
PSUMMARY_DUMP Summary,
|
||
PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This deletes non existent memory. Non existent memory is defined as the
|
||
unassigned memory between memory runs. For example, memory between
|
||
(run[0].base + size) and run[1].base.
|
||
|
||
Arguments:
|
||
|
||
pHeader - This is a pointer to the summary dump header.
|
||
|
||
dwStartingIndex - The starting bit index in the bitmap (starting page).
|
||
|
||
dwMemoryAddress - Pseudo-virtual address being mapped.
|
||
|
||
dwByteCount - The number of bytes to copy.
|
||
|
||
dwMaxBitmapBit - The limit or the highest possible valid bit in the bitmap.
|
||
|
||
pMdl - The MDL to create.
|
||
|
||
Return Value:
|
||
|
||
(none)
|
||
|
||
--*/
|
||
{
|
||
ULONG NumberOfRuns;
|
||
ULONG i;
|
||
PFN_NUMBER CurrentPageFrame;
|
||
PFN_NUMBER NextPageFrame;
|
||
PRTL_BITMAP BitMap;
|
||
PPHYSICAL_MEMORY_RUN Run;
|
||
|
||
//
|
||
// Verification
|
||
//
|
||
|
||
ASSERT ( Summary != NULL );
|
||
ASSERT ( MmPhysicalMemoryBlock != NULL );
|
||
|
||
//
|
||
// Initialization
|
||
//
|
||
|
||
BitMap = (PRTL_BITMAP) &Summary->Bitmap;
|
||
NumberOfRuns = MmPhysicalMemoryBlock->NumberOfRuns;
|
||
Run = &MmPhysicalMemoryBlock->Run[0];
|
||
|
||
|
||
i = 0;
|
||
CurrentPageFrame = 1;
|
||
NextPageFrame = Run->BasePage;
|
||
|
||
//
|
||
// Remove the gap from 0 till the first run.
|
||
//
|
||
|
||
if (NextPageFrame > CurrentPageFrame) {
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Win64 PFN_NUMBER > 32 bits.
|
||
//
|
||
// We must handle page frame numbers greater than 32 bits.
|
||
// Then remove the casts on the PageFrames below.
|
||
//
|
||
|
||
IopRemovePageFromPageMap (Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG)CurrentPageFrame,
|
||
(ULONG)(NextPageFrame-CurrentPageFrame)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Remove the gaps between runs.
|
||
//
|
||
|
||
while (i < NumberOfRuns - 1) {
|
||
|
||
CurrentPageFrame = Run->BasePage + Run->PageCount;
|
||
i++;
|
||
Run++;
|
||
|
||
//
|
||
// Get the next starting BasePage.
|
||
//
|
||
|
||
NextPageFrame = Run->BasePage;
|
||
|
||
if (NextPageFrame != CurrentPageFrame) {
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: Win64 PFN_NUMBER > 32 bits.
|
||
//
|
||
// We must handle page frame numbers greater than 32 bits.
|
||
// Then remove the casts on the page frames below.
|
||
//
|
||
|
||
IopRemovePageFromPageMap (Summary->BitmapSize,
|
||
BitMap,
|
||
(ULONG)CurrentPageFrame,
|
||
(ULONG)(NextPageFrame - CurrentPageFrame)
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
IopInvokeSecondaryDumpDataCallbacks(
|
||
IN PDUMP_DRIVER_WRITE DriverWriteRoutine,
|
||
IN OUT PLARGE_INTEGER * Mcb,
|
||
IN OUT PMDL Mdl,
|
||
IN ULONG DriverTransferSize,
|
||
IN BYTE* Buffer,
|
||
IN ULONG BufferSize,
|
||
IN ULONG MaxTotal,
|
||
IN ULONG MaxPerCallback,
|
||
OUT OPTIONAL PULONG SpaceNeeded
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Walk the list of dump data callbacks, invoking them
|
||
and writing their data out.
|
||
|
||
Arguments:
|
||
|
||
DriverWriteRoutine - The write routine for the driver.
|
||
|
||
Mcb - Message Control Block where the data is to be written.
|
||
|
||
Mdl - Address of the MDL to be filled in.
|
||
|
||
DriverTransferSize - The maximum transfer size for the driver.
|
||
|
||
Buffer - The buffer to use as a scratch buffer.
|
||
|
||
BufferSize - Size of Buffer.
|
||
|
||
MaxTotal - Maximum amount of data allowed overall.
|
||
|
||
MaxPerCallback - Maximum amount of data allowed per callback.
|
||
|
||
SpaceNeeded - Amount of data used by all callbacks. If this
|
||
argument is present then no I/O is done, just
|
||
accumulation of data sizes.
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - On success.
|
||
|
||
NTSTATUS - Otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord;
|
||
ULONG_PTR Checksum;
|
||
ULONG Index;
|
||
PLIST_ENTRY LastEntry;
|
||
PLIST_ENTRY ListHead;
|
||
PLIST_ENTRY NextEntry;
|
||
PUCHAR Source;
|
||
PDUMP_BLOB_FILE_HEADER BlobFileHdr;
|
||
PDUMP_BLOB_HEADER BlobHdr;
|
||
|
||
// Assert that basic structures preserve 8-byte alignment.
|
||
C_ASSERT((sizeof(DUMP_BLOB_FILE_HEADER) & 7) == 0);
|
||
C_ASSERT((sizeof(DUMP_BLOB_HEADER) & 7) == 0);
|
||
|
||
if (ARGUMENT_PRESENT(SpaceNeeded)) {
|
||
*SpaceNeeded = 0;
|
||
}
|
||
|
||
//
|
||
// If the caller isn't allowing a reasonable amount of
|
||
// data don't even bother to look.
|
||
//
|
||
|
||
if (MaxPerCallback < PAGE_SIZE || MaxTotal < MaxPerCallback) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// If the bug check callback listhead is not initialized, then the
|
||
// bug check has occured before the system has gotten far enough
|
||
// in the initialization code to enable anyone to register a callback.
|
||
//
|
||
|
||
ListHead = &KeBugCheckReasonCallbackListHead;
|
||
if (ListHead->Flink == NULL || ListHead->Blink == NULL) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// The current dump I/O routines only handle
|
||
// page-granular I/O so everything must be
|
||
// packed into a page for a single write.
|
||
//
|
||
// Start out with an overall file header followed
|
||
// by a blob header. After the first blob is written
|
||
// the blob header will be moved down to the head
|
||
// of the buffer.
|
||
//
|
||
|
||
BlobFileHdr = (PDUMP_BLOB_FILE_HEADER)Buffer;
|
||
BlobHdr = (PDUMP_BLOB_HEADER)(BlobFileHdr + 1);
|
||
|
||
//
|
||
// Scan the bug check callback list.
|
||
//
|
||
|
||
LastEntry = ListHead;
|
||
NextEntry = ListHead->Flink;
|
||
while (NextEntry != ListHead) {
|
||
|
||
//
|
||
// If no more dump data is allowed we're done.
|
||
//
|
||
|
||
if (!MaxTotal) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// The next entry address must be aligned properly, the
|
||
// callback record must be readable, and the callback record
|
||
// must have back link to the last entry.
|
||
//
|
||
|
||
if (((ULONG_PTR)NextEntry & (sizeof(ULONG_PTR) - 1)) != 0) {
|
||
return STATUS_DATATYPE_MISALIGNMENT;
|
||
}
|
||
|
||
CallbackRecord = CONTAINING_RECORD(NextEntry,
|
||
KBUGCHECK_REASON_CALLBACK_RECORD,
|
||
Entry);
|
||
|
||
Source = (PUCHAR)CallbackRecord;
|
||
for (Index = 0; Index < sizeof(*CallbackRecord); Index += 1) {
|
||
if (MmIsAddressValid((PVOID)Source) == FALSE) {
|
||
return STATUS_PARTIAL_COPY;
|
||
}
|
||
|
||
Source += 1;
|
||
}
|
||
|
||
if (CallbackRecord->Entry.Blink != LastEntry) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
LastEntry = NextEntry;
|
||
NextEntry = NextEntry->Flink;
|
||
|
||
//
|
||
// If the callback record has a state of inserted and the
|
||
// computed checksum matches the callback record checksum,
|
||
// then call the specified bug check callback routine.
|
||
//
|
||
|
||
Checksum = (ULONG_PTR)CallbackRecord->CallbackRoutine;
|
||
Checksum += (ULONG_PTR)CallbackRecord->Reason;
|
||
Checksum += (ULONG_PTR)CallbackRecord->Component;
|
||
if ((CallbackRecord->State != BufferInserted) ||
|
||
(CallbackRecord->Checksum != Checksum) ||
|
||
(CallbackRecord->Reason != KbCallbackSecondaryDumpData) ||
|
||
MmIsAddressValid((PVOID)(ULONG_PTR)CallbackRecord->
|
||
CallbackRoutine) == FALSE) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Call the specified bug check callback routine and
|
||
// handle any exceptions that occur.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(SpaceNeeded)) {
|
||
CallbackRecord->State = BufferStarted;
|
||
}
|
||
|
||
try {
|
||
KBUGCHECK_SECONDARY_DUMP_DATA CbArgument;
|
||
NTSTATUS Status;
|
||
ULONG BufferAvail;
|
||
|
||
// Clean the buffer before letting
|
||
// the callback have it.
|
||
RtlZeroMemory(Buffer, BufferSize);
|
||
|
||
// Start the callback's buffer after the blob header.
|
||
CbArgument.InBuffer = (PVOID)(BlobHdr + 1);
|
||
BufferAvail = BufferSize - (ULONG)
|
||
((ULONG_PTR)CbArgument.InBuffer - (ULONG_PTR)Buffer);
|
||
CbArgument.InBufferLength = BufferAvail;
|
||
CbArgument.MaximumAllowed = MaxPerCallback;
|
||
RtlZeroMemory(&CbArgument.Guid, sizeof(CbArgument.Guid));
|
||
CbArgument.OutBuffer = ARGUMENT_PRESENT(SpaceNeeded) ?
|
||
NULL : CbArgument.InBuffer;
|
||
CbArgument.OutBufferLength = 0;
|
||
|
||
(CallbackRecord->CallbackRoutine)(KbCallbackSecondaryDumpData,
|
||
CallbackRecord,
|
||
&CbArgument,
|
||
sizeof(CbArgument));
|
||
|
||
//
|
||
// If no data was used there's nothing to write.
|
||
//
|
||
|
||
if (!CbArgument.OutBuffer || !CbArgument.OutBufferLength) {
|
||
// Set this state even when sizing as
|
||
// there's no need to call again.
|
||
CallbackRecord->State = BufferFinished;
|
||
__leave;
|
||
}
|
||
|
||
//
|
||
// The callback may have used the buffer given or
|
||
// it may have returned its own buffer. If it
|
||
// used the buffer given it must be page aligned.
|
||
//
|
||
|
||
if ((PBYTE)CbArgument.OutBuffer >= Buffer &&
|
||
(PBYTE)CbArgument.OutBuffer < Buffer + BufferSize) {
|
||
|
||
if (CbArgument.OutBuffer != (PVOID)(BlobHdr + 1) ||
|
||
CbArgument.OutBufferLength > BufferAvail) {
|
||
// If too much or the wrong data was used memory has
|
||
// been trashed. Exit and hope the system still runs.
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
// The header buffer was used so we can write
|
||
// the data along with the header.
|
||
BlobHdr->PrePad = 0;
|
||
BlobHdr->PostPad = BufferAvail - CbArgument.OutBufferLength;
|
||
|
||
} else {
|
||
|
||
if (CbArgument.OutBufferLength > MaxPerCallback ||
|
||
BYTE_OFFSET(CbArgument.OutBuffer) ||
|
||
!IopIsAddressRangeValid(CbArgument.OutBuffer,
|
||
CbArgument.OutBufferLength)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
// The header buffer is separate from the data
|
||
// buffer so prepad and postpad to a page boundary.
|
||
BlobHdr->PrePad = BufferAvail;
|
||
BlobHdr->PostPad =
|
||
(ULONG)(ROUND_TO_PAGES(CbArgument.OutBufferLength) -
|
||
CbArgument.OutBufferLength);
|
||
}
|
||
|
||
//
|
||
// Write the page containing the headers.
|
||
//
|
||
|
||
if ((PBYTE)BlobHdr > Buffer) {
|
||
BlobFileHdr->Signature1 = DUMP_BLOB_SIGNATURE1;
|
||
BlobFileHdr->Signature2 = DUMP_BLOB_SIGNATURE2;
|
||
BlobFileHdr->HeaderSize = sizeof(*BlobFileHdr);
|
||
BlobFileHdr->BuildNumber = NtBuildNumber;
|
||
}
|
||
|
||
BlobHdr->HeaderSize = sizeof(*BlobHdr);
|
||
BlobHdr->Tag = CbArgument.Guid;
|
||
BlobHdr->DataSize = CbArgument.OutBufferLength;
|
||
|
||
if (ARGUMENT_PRESENT(SpaceNeeded)) {
|
||
(*SpaceNeeded) += BufferSize;
|
||
} else {
|
||
Status = IopWriteToDisk(Buffer, BufferSize,
|
||
DriverWriteRoutine,
|
||
Mcb, Mdl, DriverTransferSize,
|
||
KbDumpIoSecondaryData);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Write any extra data buffer pages.
|
||
//
|
||
|
||
if (CbArgument.OutBuffer != (PVOID)(BlobHdr + 1)) {
|
||
if (ARGUMENT_PRESENT(SpaceNeeded)) {
|
||
(*SpaceNeeded) += (ULONG)
|
||
ROUND_TO_PAGES(CbArgument.OutBufferLength);
|
||
} else {
|
||
Status = IopWriteToDisk(CbArgument.OutBuffer,
|
||
(ULONG)ROUND_TO_PAGES(CbArgument.OutBufferLength),
|
||
DriverWriteRoutine,
|
||
Mcb, Mdl, DriverTransferSize,
|
||
KbDumpIoSecondaryData);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
}
|
||
}
|
||
|
||
MaxTotal -= (ULONG)ROUND_TO_PAGES(CbArgument.OutBufferLength);
|
||
|
||
// We've written at least one blob so we don't
|
||
// need the file header any more.
|
||
BlobHdr = (PDUMP_BLOB_HEADER)Buffer;
|
||
|
||
if (!ARGUMENT_PRESENT(SpaceNeeded)) {
|
||
CallbackRecord->State = BufferFinished;
|
||
}
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
// Set this state even when sizing as
|
||
// we don't want to call a bad callback again.
|
||
CallbackRecord->State = BufferIncomplete;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
IopInvokeDumpIoCallbacks(
|
||
IN PVOID Buffer,
|
||
IN ULONG BufferLength,
|
||
IN KBUGCHECK_DUMP_IO_TYPE Type
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Walk the list of dump I/O callbacks and invoke them.
|
||
|
||
Arguments:
|
||
|
||
Buffer - The buffer of data being written.
|
||
|
||
BufferLength - Size of Buffer.
|
||
|
||
Type - Type of data being written.
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - On success.
|
||
|
||
NTSTATUS - Otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord;
|
||
ULONG_PTR Checksum;
|
||
ULONG Index;
|
||
PLIST_ENTRY LastEntry;
|
||
PLIST_ENTRY ListHead;
|
||
PLIST_ENTRY NextEntry;
|
||
PUCHAR Source;
|
||
|
||
//
|
||
// If the bug check callback listhead is not initialized, then the
|
||
// bug check has occured before the system has gotten far enough
|
||
// in the initialization code to enable anyone to register a callback.
|
||
//
|
||
|
||
ListHead = &KeBugCheckReasonCallbackListHead;
|
||
if (ListHead->Flink == NULL || ListHead->Blink == NULL) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Scan the bug check callback list.
|
||
//
|
||
|
||
LastEntry = ListHead;
|
||
NextEntry = ListHead->Flink;
|
||
while (NextEntry != ListHead) {
|
||
|
||
//
|
||
// The next entry address must be aligned properly, the
|
||
// callback record must be readable, and the callback record
|
||
// must have back link to the last entry.
|
||
//
|
||
|
||
if (((ULONG_PTR)NextEntry & (sizeof(ULONG_PTR) - 1)) != 0) {
|
||
return STATUS_DATATYPE_MISALIGNMENT;
|
||
}
|
||
|
||
CallbackRecord = CONTAINING_RECORD(NextEntry,
|
||
KBUGCHECK_REASON_CALLBACK_RECORD,
|
||
Entry);
|
||
|
||
Source = (PUCHAR)CallbackRecord;
|
||
for (Index = 0; Index < sizeof(*CallbackRecord); Index += 1) {
|
||
if (MmIsAddressValid((PVOID)Source) == FALSE) {
|
||
return STATUS_PARTIAL_COPY;
|
||
}
|
||
|
||
Source += 1;
|
||
}
|
||
|
||
if (CallbackRecord->Entry.Blink != LastEntry) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
LastEntry = NextEntry;
|
||
NextEntry = NextEntry->Flink;
|
||
|
||
//
|
||
// If the callback record has a state of inserted and the
|
||
// computed checksum matches the callback record checksum,
|
||
// then call the specified bug check callback routine.
|
||
//
|
||
|
||
Checksum = (ULONG_PTR)CallbackRecord->CallbackRoutine;
|
||
Checksum += (ULONG_PTR)CallbackRecord->Reason;
|
||
Checksum += (ULONG_PTR)CallbackRecord->Component;
|
||
if ((CallbackRecord->State != BufferInserted) ||
|
||
(CallbackRecord->Checksum != Checksum) ||
|
||
(CallbackRecord->Reason != KbCallbackDumpIo) ||
|
||
MmIsAddressValid((PVOID)(ULONG_PTR)CallbackRecord->
|
||
CallbackRoutine) == FALSE) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Call the specified bug check callback routine and
|
||
// handle any exceptions that occur.
|
||
//
|
||
|
||
try {
|
||
KBUGCHECK_DUMP_IO CbArgument;
|
||
|
||
// Currently we aren't allowing arbitrary I/O
|
||
// so always use the special sequential I/O offset.
|
||
CbArgument.Offset = (ULONG64)-1;
|
||
CbArgument.Buffer = Buffer;
|
||
CbArgument.BufferLength = BufferLength;
|
||
CbArgument.Type = Type;
|
||
|
||
(CallbackRecord->CallbackRoutine)(KbCallbackDumpIo,
|
||
CallbackRecord,
|
||
&CbArgument,
|
||
sizeof(CbArgument));
|
||
|
||
if (Type == KbDumpIoComplete) {
|
||
CallbackRecord->State = BufferFinished;
|
||
}
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
CallbackRecord->State = BufferIncomplete;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IopCompleteDumpInitialization(
|
||
IN HANDLE FileHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked after the dump file is created.
|
||
|
||
The purpose is to obtain the retrieval pointers so that they can then be
|
||
used later to write the dump. The last step is to checksum the
|
||
IopDumpControlBlock.
|
||
|
||
Fields in the IopDumpControlBlock are updated if necessary and the
|
||
IopDumpControlBlockChecksum is initialized.
|
||
|
||
This is the final step in dump initialization.
|
||
|
||
Arguments:
|
||
|
||
FileHandle - Handle to the dump file just created.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates success.
|
||
|
||
Other NTSTATUS - Failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
NTSTATUS ErrorToLog;
|
||
ULONG i;
|
||
LARGE_INTEGER RequestedFileSize;
|
||
PLARGE_INTEGER mcb;
|
||
PFILE_OBJECT FileObject;
|
||
PDEVICE_OBJECT DeviceObject;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_STANDARD_INFORMATION StandardFileInfo;
|
||
ULONG MaxSecondaryData;
|
||
ULONG MaxSecondaryCbData;
|
||
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
ErrorToLog = STATUS_SUCCESS; // No error
|
||
FileObject = NULL;
|
||
DeviceObject = NULL;
|
||
|
||
Status = ObReferenceObjectByHandle( FileHandle,
|
||
0,
|
||
IoFileObjectType,
|
||
KernelMode,
|
||
(PVOID *) &FileObject,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_SUCCESS (Status) ) {
|
||
ASSERT (FALSE);
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
DeviceObject = FileObject->DeviceObject;
|
||
|
||
//
|
||
// If this device object represents the boot partition then query
|
||
// the retrieval pointers for the file.
|
||
//
|
||
|
||
if ( !(DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) ) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Cannot dump to pagefile on non-system partition.\n"
|
||
" DO = %p\n",
|
||
DeviceObject));
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = ZwQueryInformationFile(
|
||
FileHandle,
|
||
&IoStatusBlock,
|
||
&StandardFileInfo,
|
||
sizeof (StandardFileInfo),
|
||
FileStandardInformation
|
||
);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
Status = KeWaitForSingleObject( &FileObject->Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
Status = IoStatusBlock.Status;
|
||
}
|
||
|
||
if ( !NT_SUCCESS (Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Ask for as much space as needed for the basic dump
|
||
// plus extra space for secondary dump data.
|
||
//
|
||
|
||
RequestedFileSize = IopDumpControlBlock->DumpFileSize;
|
||
|
||
IopGetSecondaryDumpDataLimits(IopDumpControlBlock->Flags,
|
||
&MaxSecondaryData, &MaxSecondaryCbData);
|
||
|
||
RequestedFileSize.QuadPart += MaxSecondaryData;
|
||
|
||
//
|
||
// Do not ask for more space than is in the pagefile.
|
||
//
|
||
|
||
if (RequestedFileSize.QuadPart > StandardFileInfo.EndOfFile.QuadPart) {
|
||
RequestedFileSize = StandardFileInfo.EndOfFile;
|
||
}
|
||
|
||
Status = ZwFsControlFile(
|
||
FileHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_QUERY_RETRIEVAL_POINTERS,
|
||
&RequestedFileSize,
|
||
sizeof( LARGE_INTEGER ),
|
||
&mcb,
|
||
sizeof( PVOID )
|
||
);
|
||
|
||
if (Status == STATUS_PENDING) {
|
||
Status = KeWaitForSingleObject( &FileObject->Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
Status = IoStatusBlock.Status;
|
||
}
|
||
|
||
|
||
//
|
||
// NOTE: If you fail here, put a BP on ntfs!NtfsQueryRetrievalPointers
|
||
// or FatQueryRetrievalPointers and see why you failed.
|
||
//
|
||
|
||
if ( !NT_SUCCESS (Status) ) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: ZwFsControlFile returnd %d\n"
|
||
" File = %p\n",
|
||
FileObject,
|
||
Status
|
||
));
|
||
ErrorToLog = IO_DUMP_POINTER_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// This paging file is on the system boot partition, and
|
||
// the retrieval pointers for the file were just successfully
|
||
// queried. Walk the MCB to size it, and then checksum it.
|
||
//
|
||
|
||
for (i = 0; mcb [i].QuadPart; i++) {
|
||
NOTHING;
|
||
}
|
||
|
||
//
|
||
// Write back fields of the IopDumpControlBlock.
|
||
//
|
||
|
||
IopDumpControlBlock->FileDescriptorArray = mcb;
|
||
IopDumpControlBlock->FileDescriptorSize = (i + 1) * sizeof (LARGE_INTEGER);
|
||
IopDumpControlBlock->DumpFileSize = RequestedFileSize;
|
||
IopDumpControlBlockChecksum = IopGetDumpControlBlockCheck ( IopDumpControlBlock );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Cleanup:
|
||
|
||
if (Status != STATUS_SUCCESS &&
|
||
ErrorToLog != STATUS_SUCCESS ) {
|
||
|
||
IopLogErrorEvent (0,
|
||
4,
|
||
STATUS_SUCCESS,
|
||
ErrorToLog,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
DeviceObject = NULL;
|
||
|
||
if ( FileObject ) {
|
||
ObDereferenceObject( FileObject );
|
||
FileObject = NULL;
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
IopFreeDCB(
|
||
BOOLEAN FreeDCB
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free dump control block storage.
|
||
|
||
Arguments:
|
||
|
||
FreeDCB - Implictly free storage for the dump control block.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PDUMP_CONTROL_BLOCK dcb;
|
||
|
||
dcb = IopDumpControlBlock;
|
||
|
||
if (dcb) {
|
||
|
||
if (dcb->HeaderPage) {
|
||
ExFreePool (dcb->HeaderPage);
|
||
dcb->HeaderPage = NULL;
|
||
}
|
||
|
||
if (dcb->FileDescriptorArray) {
|
||
ExFreePool (dcb->FileDescriptorArray);
|
||
dcb->FileDescriptorArray = NULL;
|
||
}
|
||
|
||
if (dcb->DumpStack) {
|
||
IoFreeDumpStack (dcb->DumpStack);
|
||
dcb->DumpStack = NULL;
|
||
}
|
||
|
||
if (dcb->TriageDumpBuffer) {
|
||
ExFreePool (dcb->TriageDumpBuffer);
|
||
dcb->TriageDumpBuffer = NULL;
|
||
dcb->TriageDumpBufferSize = 0;
|
||
}
|
||
|
||
//
|
||
// Disable all options that require dump file access
|
||
//
|
||
|
||
dcb->Flags = 0;
|
||
|
||
if (FreeDCB) {
|
||
IopDumpControlBlock = NULL;
|
||
ExFreePool( dcb );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IoConfigureCrashDump(
|
||
CRASHDUMP_CONFIGURATION Configuration
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Change the configuration of the crashdump for this machine.
|
||
|
||
Arguments:
|
||
|
||
Configuration - What to change the configuration to.
|
||
|
||
CrashDumpDisable - Disable crashdumps for this machine.
|
||
|
||
CrashDumpReconfigure - Reread crashdump settings from the
|
||
registry and apply them.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE FileHandle;
|
||
PKTHREAD CurrentThread;
|
||
|
||
CurrentThread = KeGetCurrentThread ();
|
||
KeEnterCriticalRegionThread(CurrentThread);
|
||
switch (Configuration) {
|
||
|
||
case CrashDumpDisable:
|
||
if (ExAcquireResourceExclusiveLite(&IopCrashDumpLock, FALSE)) {
|
||
IopFreeDCB (FALSE);
|
||
ExReleaseResourceLite(&IopCrashDumpLock);
|
||
}
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case CrashDumpReconfigure:
|
||
FileHandle = MmGetSystemPageFile();
|
||
if (FileHandle == NULL) {
|
||
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
} else {
|
||
ExAcquireResourceExclusiveLite(&IopCrashDumpLock,TRUE);
|
||
if (IoInitializeCrashDump(FileHandle)) {
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
ExReleaseResourceLite(&IopCrashDumpLock);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
KeLeaveCriticalRegionThread(CurrentThread);
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
IopReadDumpRegistry(
|
||
OUT PULONG dumpControl,
|
||
OUT PULONG numberOfHeaderPages,
|
||
OUT PULONG autoReboot,
|
||
OUT PULONG dumpFileSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads the dump parameters from the registry.
|
||
|
||
Arguments:
|
||
|
||
dumpControl - Supplies a pointer to the dumpControl flags to set.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE keyHandle;
|
||
HANDLE crashHandle;
|
||
LOGICAL crashHandleOpened;
|
||
UNICODE_STRING keyName;
|
||
NTSTATUS status;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
ULONG handleValue;
|
||
|
||
*dumpControl = 0;
|
||
*autoReboot = 0;
|
||
*dumpFileSize = 0;
|
||
|
||
*numberOfHeaderPages = BYTES_TO_PAGES(sizeof(DUMP_HEADER));
|
||
|
||
//
|
||
// Begin by opening the path to the control for dumping memory. Note
|
||
// that if it does not exist, then no dumps will occur.
|
||
//
|
||
|
||
crashHandleOpened = FALSE;
|
||
|
||
RtlInitUnicodeString( &keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control" );
|
||
|
||
status = IopOpenRegistryKey( &keyHandle,
|
||
(HANDLE) NULL,
|
||
&keyName,
|
||
KEY_READ,
|
||
FALSE );
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
return;
|
||
}
|
||
|
||
RtlInitUnicodeString( &keyName, L"CrashControl" );
|
||
status = IopOpenRegistryKey( &crashHandle,
|
||
keyHandle,
|
||
&keyName,
|
||
KEY_READ,
|
||
FALSE );
|
||
|
||
NtClose( keyHandle );
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
return;
|
||
}
|
||
|
||
crashHandleOpened = TRUE;
|
||
|
||
//
|
||
// Now get the value of the crash control to determine whether or not
|
||
// dumping is enabled.
|
||
//
|
||
|
||
status = IopGetRegistryValue( crashHandle,
|
||
L"CrashDumpEnabled",
|
||
&keyValueInformation );
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
if (keyValueInformation->DataLength) {
|
||
|
||
handleValue = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset));
|
||
ExFreePool( keyValueInformation );
|
||
|
||
if (handleValue) {
|
||
|
||
*dumpControl |= DCB_DUMP_ENABLED;
|
||
|
||
if ( handleValue == 3 ) {
|
||
|
||
*dumpControl |= DCB_TRIAGE_DUMP_ENABLED;
|
||
|
||
} else if ( handleValue == 4 ) {
|
||
|
||
*dumpControl |= ( DCB_TRIAGE_DUMP_ENABLED | DCB_TRIAGE_DUMP_ACT_UPON_ENABLED );
|
||
|
||
} else if ( handleValue == 2 ) {
|
||
|
||
*dumpControl |= DCB_SUMMARY_DUMP_ENABLED;
|
||
|
||
//
|
||
// Allocate enough storage for the dump header, summary
|
||
// dump header and bitmap.
|
||
//
|
||
|
||
*numberOfHeaderPages = (ULONG) BYTES_TO_PAGES(
|
||
sizeof(DUMP_HEADER) +
|
||
((MmPhysicalMemoryBlock->NumberOfPages / 8) + 1) +
|
||
sizeof (SUMMARY_DUMP));
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
status = IopGetRegistryValue( crashHandle,
|
||
L"LogEvent",
|
||
&keyValueInformation );
|
||
|
||
if (NT_SUCCESS( status )) {
|
||
if (keyValueInformation->DataLength) {
|
||
handleValue = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset));
|
||
ExFreePool( keyValueInformation);
|
||
if (handleValue) {
|
||
*dumpControl |= DCB_SUMMARY_ENABLED;
|
||
}
|
||
}
|
||
}
|
||
|
||
status = IopGetRegistryValue( crashHandle,
|
||
L"SendAlert",
|
||
&keyValueInformation );
|
||
|
||
if (NT_SUCCESS( status )) {
|
||
if (keyValueInformation->DataLength) {
|
||
handleValue = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset));
|
||
ExFreePool( keyValueInformation);
|
||
if (handleValue) {
|
||
*dumpControl |= DCB_SUMMARY_ENABLED;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now determine whether or not automatic reboot is enabled.
|
||
//
|
||
|
||
status = IopGetRegistryValue( crashHandle,
|
||
L"AutoReboot",
|
||
&keyValueInformation );
|
||
|
||
|
||
if (NT_SUCCESS( status )) {
|
||
if (keyValueInformation->DataLength) {
|
||
*autoReboot = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset));
|
||
}
|
||
ExFreePool( keyValueInformation );
|
||
}
|
||
|
||
//
|
||
// If we aren't auto rebooting or crashing then return now.
|
||
//
|
||
|
||
if (*dumpControl == 0 && *autoReboot == 0) {
|
||
if (crashHandleOpened == TRUE) {
|
||
NtClose( crashHandle );
|
||
}
|
||
return;
|
||
}
|
||
|
||
status = IopGetRegistryValue( crashHandle,
|
||
L"DumpFileSize",
|
||
&keyValueInformation );
|
||
|
||
if (NT_SUCCESS( status )) {
|
||
if (keyValueInformation->DataLength) {
|
||
*dumpFileSize = * ((PULONG) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset));
|
||
}
|
||
|
||
ExFreePool( keyValueInformation );
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IopInitializeDCB(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the Dump Control Block (DCB). It allocates the
|
||
DCB and reads the crashdump parameters from the registry.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
The final function value is TRUE if everything worked, else FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDUMP_CONTROL_BLOCK dcb;
|
||
ULONG dumpControl;
|
||
ULONG dcbSize;
|
||
LARGE_INTEGER page;
|
||
ULONG numberOfHeaderPages;
|
||
ULONG dumpFileSize;
|
||
|
||
//
|
||
// Read all the registry default values first.
|
||
//
|
||
|
||
IopReadDumpRegistry ( &dumpControl,
|
||
&numberOfHeaderPages,
|
||
&IopAutoReboot,
|
||
&dumpFileSize);
|
||
|
||
//
|
||
// If we aren't crashing or auto rebooting then return now.
|
||
//
|
||
|
||
if (dumpControl == 0 && IopAutoReboot == 0) {
|
||
|
||
//
|
||
// At some point, we will conditionally on system size, type, etc,
|
||
// set dump defaults like the below and skip over the return.
|
||
//
|
||
// *dumpControl = (DCB_DUMP_ENABLED | DCB_TRIAGE_DUMP_ENABLED);
|
||
// *IopAutoReboot = 1;
|
||
// *dumpFileSize = ?
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (dumpControl & DCB_TRIAGE_DUMP_ENABLED) {
|
||
dumpControl &= ~DCB_SUMMARY_ENABLED;
|
||
dumpFileSize = TRIAGE_DUMP_SIZE;
|
||
}
|
||
|
||
//
|
||
// Allocate and initialize the structures necessary to describe and control
|
||
// the post-bugcheck code.
|
||
//
|
||
|
||
dcbSize = sizeof( DUMP_CONTROL_BLOCK ) + sizeof( MINIPORT_NODE );
|
||
dcb = ExAllocatePool( NonPagedPool, dcbSize );
|
||
|
||
if (!dcb) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Not enough pool to allocate DCB %d\n",
|
||
__LINE__
|
||
));
|
||
|
||
IopLogErrorEvent(0,1,STATUS_SUCCESS,IO_DUMP_INITIALIZATION_FAILURE,0,NULL,0,NULL);
|
||
return FALSE;
|
||
}
|
||
|
||
RtlZeroMemory( dcb, dcbSize );
|
||
dcb->Type = IO_TYPE_DCB;
|
||
dcb->Size = (USHORT) dcbSize;
|
||
dcb->Flags = (UCHAR) dumpControl;
|
||
dcb->NumberProcessors = KeNumberProcessors;
|
||
dcb->ProcessorArchitecture = KeProcessorArchitecture;
|
||
dcb->MinorVersion = (USHORT) NtBuildNumber;
|
||
dcb->MajorVersion = (USHORT) ((NtBuildNumber >> 28) & 0xfffffff);
|
||
dcb->BuildNumber = CmNtCSDVersion;
|
||
dcb->TriageDumpFlags = TRIAGE_DUMP_BASIC_INFO | TRIAGE_DUMP_MMINFO |
|
||
TRIAGE_DUMP_DEBUGGER_DATA | TRIAGE_DUMP_DATA_BLOCKS;
|
||
|
||
dcb->DumpFileSize.QuadPart = dumpFileSize;
|
||
|
||
//
|
||
// Allocate header page.
|
||
//
|
||
|
||
dcb->HeaderSize = numberOfHeaderPages * PAGE_SIZE;
|
||
dcb->HeaderPage = ExAllocatePool( NonPagedPool, dcb->HeaderSize );
|
||
|
||
if (!dcb->HeaderPage) {
|
||
ExFreePool (dcb);
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Not enough pool to allocate DCB %d\n",
|
||
__LINE__
|
||
));
|
||
IopLogErrorEvent(0,1,STATUS_SUCCESS,IO_DUMP_INITIALIZATION_FAILURE,0,NULL,0,NULL);
|
||
return FALSE;
|
||
}
|
||
page = MmGetPhysicalAddress( dcb->HeaderPage );
|
||
dcb->HeaderPfn = (ULONG)(page.QuadPart >> PAGE_SHIFT);
|
||
|
||
|
||
//
|
||
// Allocate the triage-dump buffer.
|
||
//
|
||
|
||
if (dumpControl & DCB_TRIAGE_DUMP_ENABLED) {
|
||
|
||
dcb->TriageDumpBuffer = ExAllocatePool (
|
||
NonPagedPool,
|
||
dumpFileSize
|
||
);
|
||
|
||
if (!dcb->TriageDumpBuffer) {
|
||
ExFreePool (dcb->HeaderPage);
|
||
ExFreePool (dcb);
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Not enough pool to allocate DCB %d\n",
|
||
__LINE__
|
||
));
|
||
IopLogErrorEvent(0,1,STATUS_SUCCESS,IO_DUMP_INITIALIZATION_FAILURE,0,NULL,0,NULL);
|
||
return FALSE;
|
||
}
|
||
|
||
dcb->TriageDumpBufferSize = dumpFileSize;
|
||
}
|
||
|
||
IopDumpControlBlock = dcb;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
IoInitializeCrashDump(
|
||
IN HANDLE hPageFile
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine configures the system for crash dump. The following things
|
||
are done:
|
||
|
||
1. Initialize the dump control block and init registry crashdump
|
||
parameters.
|
||
|
||
2. Configure either page or fast dump.
|
||
|
||
3. Complete dump file initialization.
|
||
|
||
This routine is called as each page file is created. A return value of
|
||
TRUE tells the caller (i.e., NtCreatePagingFiles, IoPageFileCreated)
|
||
that crash dump has been configured.
|
||
|
||
|
||
Arguments:
|
||
|
||
hPageFile - Handle to the paging file
|
||
|
||
Return Value:
|
||
|
||
TRUE - Configuration complete (or crash dump not enabled).
|
||
|
||
FALSE - Error, retry PageFile is not on boot partition.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS dwStatus;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
|
||
//
|
||
// If crashdump is already enabled, free it and reinitialize with the
|
||
// current settings.
|
||
//
|
||
IopFreeDCB (TRUE);
|
||
if (!IopInitializeDCB()) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Return crash dump not enabled
|
||
//
|
||
if (!IopDumpControlBlock) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// No dump enabled?
|
||
//
|
||
|
||
if ( !( IopDumpControlBlock->Flags & (DCB_DUMP_ENABLED | DCB_SUMMARY_ENABLED) ) ) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Configure the paging file for crash dump.
|
||
//
|
||
|
||
dwStatus = ObReferenceObjectByHandle(
|
||
hPageFile,
|
||
0,
|
||
IoFileObjectType,
|
||
KernelMode,
|
||
(PVOID *) &fileObject,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS( dwStatus )) {
|
||
goto error_return;
|
||
}
|
||
|
||
//
|
||
// Get a pointer to the device object for this file. Note that it
|
||
// cannot go away, since there is an open handle to it, so it is
|
||
// OK to dereference it and then use it.
|
||
//
|
||
|
||
deviceObject = fileObject->DeviceObject;
|
||
|
||
ObDereferenceObject( fileObject );
|
||
|
||
//
|
||
// This should never be called on devices that are not the boot partition
|
||
//
|
||
ASSERT(deviceObject->Flags & DO_SYSTEM_BOOT_PARTITION);
|
||
|
||
//
|
||
// Load paging file dump stack
|
||
//
|
||
|
||
dwStatus = IoGetDumpStack (L"dump_",
|
||
&IopDumpControlBlock->DumpStack,
|
||
DeviceUsageTypeDumpFile,
|
||
FALSE);
|
||
|
||
if (!NT_SUCCESS(dwStatus)) {
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Could not load dump stack status = %x\n",
|
||
dwStatus
|
||
));
|
||
goto error_return;
|
||
}
|
||
|
||
IopDumpControlBlock->DumpStack->Init.CrashDump = TRUE;
|
||
|
||
IopDumpControlBlock->DumpStack->Init.MemoryBlock = ExAllocatePool (
|
||
NonPagedPool,
|
||
IO_DUMP_MEMORY_BLOCK_PAGES * PAGE_SIZE
|
||
);
|
||
|
||
if (!IopDumpControlBlock->DumpStack->Init.MemoryBlock) {
|
||
dwStatus = STATUS_NO_MEMORY;
|
||
goto error_return;
|
||
}
|
||
|
||
|
||
//
|
||
// Calculate the amount of space required for the dump
|
||
//
|
||
IopDumpControlBlock->DumpFileSize =
|
||
IopCalculateRequiredDumpSpace(
|
||
IopDumpControlBlock->Flags,
|
||
IopDumpControlBlock->HeaderSize,
|
||
MmPhysicalMemoryBlock->NumberOfPages,
|
||
MmPhysicalMemoryBlock->NumberOfPages
|
||
);
|
||
|
||
|
||
//
|
||
// Complete dump initialization
|
||
//
|
||
|
||
dwStatus = IopCompleteDumpInitialization(hPageFile);
|
||
|
||
error_return:
|
||
|
||
//
|
||
// The BOOT partition paging file could not be configured.
|
||
// 1. Log an error message
|
||
// 2. Return TRUE so that MM does not try again
|
||
//
|
||
|
||
if (!NT_SUCCESS(dwStatus)) {
|
||
|
||
KdPrintEx ((DPFLTR_CRASHDUMP_ID,
|
||
CRASHDUMP_ERROR,
|
||
"CRASHDUMP: Page File dump init FAILED status = %x\n",
|
||
dwStatus
|
||
));
|
||
IopLogErrorEvent(0, 3, STATUS_SUCCESS, IO_DUMP_PAGE_CONFIG_FAILED, 0, NULL, 0, NULL);
|
||
|
||
//
|
||
// ISSUE - 2000/02/07 - math: IopFreeDCB probably wrong.
|
||
//
|
||
// Is the call to IopFreeDCB() correct here? Doesn't this prevent
|
||
// other dump types from working properly? Review this.
|
||
//
|
||
|
||
IopFreeDCB(FALSE);
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#define TRIAGE_DUMP_DATA_SIZE (TRIAGE_DUMP_SIZE - sizeof(ULONG))
|
||
|
||
static
|
||
BOOLEAN
|
||
IopValidateSectionSize(
|
||
ULONG Offset,
|
||
ULONG* pSectionSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks whether specified section size will overflow the dump buffer
|
||
(used just for creation of live minidumps)
|
||
|
||
Arguments:
|
||
|
||
Offset - Section offset
|
||
pSectionSize - Section size (will be changed to fit the dump)
|
||
pbOverflow - used to return the overflow status
|
||
|
||
Return Value:
|
||
|
||
TRUE - if the section fits the dump, otherwise section size will decreased
|
||
and FALSE will be returned
|
||
|
||
--*/
|
||
|
||
{
|
||
if ((Offset + *pSectionSize) < TRIAGE_DUMP_DATA_SIZE) return TRUE;
|
||
|
||
*pSectionSize = (Offset < TRIAGE_DUMP_DATA_SIZE) ?
|
||
(TRIAGE_DUMP_DATA_SIZE - Offset) : 0;
|
||
return FALSE;
|
||
}
|
||
|
||
static
|
||
ULONG
|
||
IopGetMaxValidSectionSize(
|
||
ULONG_PTR Base,
|
||
ULONG MaxSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets maximum valid memory section size less than SectionMaxSize
|
||
|
||
Arguments:
|
||
|
||
Base - beginning of the section
|
||
MaxSize - Maximum size of the section
|
||
|
||
Return Value:
|
||
|
||
Size of the continuously valid memory starting from SectionBase up to
|
||
SectionMaxSize
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Size = 0;
|
||
|
||
// XXX olegk - optimize it later to iterate by page size
|
||
while ((Size < MaxSize) && (MmIsAddressValid((PVOID)(Base + Size))))
|
||
++Size;
|
||
|
||
return Size;
|
||
}
|
||
|
||
static
|
||
ULONG
|
||
IopGetMaxValidSectionSizeDown(
|
||
ULONG_PTR Base,
|
||
ULONG MaxSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets maximum valid memory section size less than SectionMaxSize
|
||
|
||
Arguments:
|
||
|
||
Base - End of the section
|
||
MaxSize - Maximum size of the section
|
||
|
||
Return Value:
|
||
|
||
Size of the continuously valid memory ending at SectionBase downto to
|
||
SectionMaxSize
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Size = 0;
|
||
|
||
if ((ULONG_PTR)Base < (ULONG_PTR)MaxSize) MaxSize = (ULONG)Base;
|
||
|
||
// XXX olegk - optimize it later to iterate by page size
|
||
while ((Size < MaxSize) && (MmIsAddressValid((PVOID)(Base - Size))))
|
||
++Size;
|
||
|
||
return Size;
|
||
}
|
||
|
||
ULONG
|
||
KeCapturePersistentThreadState(
|
||
PCONTEXT pContext,
|
||
PETHREAD pThread,
|
||
ULONG ulBugCheckCode,
|
||
ULONG_PTR ulpBugCheckParam1,
|
||
ULONG_PTR ulpBugCheckParam2,
|
||
ULONG_PTR ulpBugCheckParam3,
|
||
ULONG_PTR ulpBugCheckParam4,
|
||
PVOID pvDump
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates main portion of the minidump in the specified buffer
|
||
This function can be used to crate live minidump (originaly designed to
|
||
work with EA recovery for video drivers)
|
||
|
||
Arguments:
|
||
|
||
pContext - context of failed thread
|
||
pThread - failed thread object (NULL means current thread)
|
||
ulBugCheckCode,
|
||
ulpBugCheckParam1,
|
||
ulpBugCheckParam2,
|
||
ulpBugCheckParam3,
|
||
ulpBugCheckParam4 - Bugcheck info
|
||
pModules - List of te loaded modules
|
||
pDump - Memory buffer to write dump context (Size of the buffer should
|
||
be at least TRIAGE_DUMP_SIZE
|
||
|
||
Return Value:
|
||
|
||
Actual size of the dump file to save on disk (always at least TRIAGE_DUMP_SIZE)
|
||
|
||
--*/
|
||
|
||
{
|
||
PMEMORY_DUMP pDump = (PMEMORY_DUMP)pvDump;
|
||
PDUMP_HEADER pdh = &(pDump->Header);
|
||
PTRIAGE_DUMP ptdh = &(pDump->Triage);
|
||
ULONG Offset = 0, SectionSize = 0;
|
||
PKDDEBUGGER_DATA64 pKdDebuggerDataBlock = (PKDDEBUGGER_DATA64)KdGetDataBlock();
|
||
PEPROCESS pProcess;
|
||
|
||
if (!pvDump) return 0;
|
||
|
||
if (!pThread) pThread = PsGetCurrentThread();
|
||
pProcess = (PEPROCESS)pThread->Tcb.ApcState.Process;
|
||
|
||
RtlZeroMemory(pDump, TRIAGE_DUMP_SIZE);
|
||
|
||
//
|
||
// Fill the dump header with signature
|
||
//
|
||
|
||
RtlFillMemoryUlong(pdh, sizeof(*pdh), DUMP_SIGNATURE);
|
||
|
||
pdh->Signature = DUMP_SIGNATURE;
|
||
pdh->ValidDump = DUMP_VALID_DUMP;
|
||
|
||
pdh->MinorVersion = (USHORT) NtBuildNumber;
|
||
pdh->MajorVersion = (USHORT) ((NtBuildNumber >> 28) & 0xfffffff);
|
||
|
||
#if defined (_IA64_)
|
||
pdh->DirectoryTableBase = MmSystemParentTablePage << PAGE_SHIFT;
|
||
#else
|
||
pdh->DirectoryTableBase = pThread->Tcb.ApcState.Process->DirectoryTableBase[0];
|
||
#endif
|
||
|
||
pdh->PfnDataBase = (ULONG_PTR)MmPfnDatabase;
|
||
pdh->PsLoadedModuleList = (ULONG_PTR)&PsLoadedModuleList;
|
||
pdh->PsActiveProcessHead = (ULONG_PTR)&PsActiveProcessHead;
|
||
|
||
pdh->MachineImageType = CURRENT_IMAGE_TYPE();
|
||
pdh->NumberProcessors = KeNumberProcessors;
|
||
|
||
pdh->BugCheckCode = ulBugCheckCode;
|
||
pdh->BugCheckParameter1 = ulpBugCheckParam1;
|
||
pdh->BugCheckParameter2 = ulpBugCheckParam2;
|
||
pdh->BugCheckParameter3 = ulpBugCheckParam3;
|
||
pdh->BugCheckParameter4 = ulpBugCheckParam4;
|
||
|
||
#if defined (_X86_)
|
||
pdh->PaeEnabled = X86PaeEnabled ();
|
||
#endif
|
||
|
||
pdh->Exception.ExceptionCode = STATUS_BREAKPOINT; // XXX olegk - ???
|
||
pdh->Exception.ExceptionRecord = 0;
|
||
pdh->Exception.NumberParameters = 0;
|
||
pdh->Exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
||
pdh->Exception.ExceptionAddress = PROGRAM_COUNTER (pContext);
|
||
|
||
pdh->RequiredDumpSpace.QuadPart = TRIAGE_DUMP_SIZE;
|
||
|
||
pdh->SystemTime.LowPart = SharedUserData->SystemTime.LowPart;
|
||
pdh->SystemTime.HighPart = SharedUserData->SystemTime.High1Time;
|
||
|
||
pdh->SystemUpTime.LowPart = SharedUserData->InterruptTime.LowPart;
|
||
pdh->SystemUpTime.HighPart = SharedUserData->InterruptTime.High1Time;
|
||
|
||
pdh->DumpType = DUMP_TYPE_TRIAGE;
|
||
pdh->MiniDumpFields = TRIAGE_DUMP_EXCEPTION |
|
||
TRIAGE_DUMP_BROKEN_DRIVER; // XXX olegk - debugger need it for memory mapping
|
||
|
||
pdh->ProductType = SharedUserData->NtProductType;
|
||
pdh->SuiteMask = SharedUserData->SuiteMask;
|
||
|
||
//
|
||
// TRIAGE header
|
||
//
|
||
|
||
ptdh->TriageOptions = 0;
|
||
ptdh->ServicePackBuild = CmNtCSDVersion;
|
||
ptdh->SizeOfDump = TRIAGE_DUMP_SIZE;
|
||
ptdh->ExceptionOffset = FIELD_OFFSET(DUMP_HEADER, Exception);
|
||
|
||
ptdh->BrokenDriverOffset = 0;
|
||
|
||
Offset = sizeof(DUMP_HEADER) + sizeof(TRIAGE_DUMP);
|
||
|
||
//
|
||
// Context
|
||
//
|
||
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_CONTEXT;
|
||
ptdh->ContextOffset = FIELD_OFFSET (DUMP_HEADER, ContextRecord);
|
||
RtlCopyMemory(pdh->ContextRecord, pContext, sizeof(CONTEXT));
|
||
|
||
//
|
||
// Save debugger data block
|
||
//
|
||
|
||
SectionSize = sizeof(KDDEBUGGER_DATA64);
|
||
if (IopValidateSectionSize(ALIGN_8(Offset), &SectionSize)) {
|
||
Offset = ALIGN_8(Offset);
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_DEBUGGER_DATA;
|
||
pdh->KdDebuggerDataBlock = (LONG_PTR)pKdDebuggerDataBlock;
|
||
ptdh->DebuggerDataOffset = Offset;
|
||
ptdh->DebuggerDataSize = sizeof(KDDEBUGGER_DATA64);
|
||
RtlCopyMemory((char*)pDump + Offset,
|
||
pKdDebuggerDataBlock,
|
||
SectionSize);
|
||
Offset += SectionSize;
|
||
}
|
||
|
||
//
|
||
// Write the PRCB.
|
||
//
|
||
|
||
SectionSize = sizeof(KPRCB);
|
||
if (IopValidateSectionSize(ALIGN_8(Offset), &SectionSize)) {
|
||
Offset = ALIGN_8(Offset);
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_PRCB;
|
||
ptdh->PrcbOffset = Offset;
|
||
RtlCopyMemory((char*)pDump + Offset,
|
||
KeGetCurrentPrcb(),
|
||
SectionSize);
|
||
Offset += SectionSize;
|
||
}
|
||
|
||
//
|
||
// Write the EPROCESS
|
||
//
|
||
|
||
SectionSize = sizeof(EPROCESS);
|
||
if (IopValidateSectionSize(ALIGN_8(Offset), &SectionSize)) {
|
||
Offset = ALIGN_8(Offset);
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_PROCESS;
|
||
ptdh->ProcessOffset = Offset;
|
||
RtlCopyMemory((char*)pDump + Offset,
|
||
pThread->Tcb.ApcState.Process,
|
||
SectionSize);
|
||
Offset += SectionSize;
|
||
}
|
||
|
||
//
|
||
// Write the ETHREAD
|
||
//
|
||
|
||
SectionSize = sizeof(ETHREAD);
|
||
if (IopValidateSectionSize(ALIGN_8(Offset), &SectionSize)) {
|
||
Offset = ALIGN_8(Offset);
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_THREAD;
|
||
ptdh->ThreadOffset = Offset;
|
||
RtlCopyMemory((PUCHAR)pDump + Offset,
|
||
pThread,
|
||
SectionSize);
|
||
Offset += SectionSize;
|
||
}
|
||
|
||
//
|
||
// Call Stack (and backing store on ia64)
|
||
//
|
||
|
||
if (pThread->Tcb.KernelStackResident) {
|
||
ULONG_PTR StackBase = (ULONG_PTR)pThread->Tcb.StackBase;
|
||
ULONG_PTR StackLimit = (ULONG_PTR)pThread->Tcb.StackLimit;
|
||
ULONG_PTR StackTop = STACK_POINTER(pContext);
|
||
|
||
if ((StackLimit > StackTop) || (StackTop >= StackBase))
|
||
StackTop = (ULONG_PTR)pThread->Tcb.StackLimit;
|
||
|
||
SectionSize = (StackBase > StackTop) ? (ULONG)(StackBase - StackTop) : 0;
|
||
SectionSize = min(SectionSize, MAX_TRIAGE_STACK_SIZE - 1);
|
||
SectionSize = IopGetMaxValidSectionSize(StackTop, SectionSize);
|
||
|
||
if (SectionSize) {
|
||
if (!IopValidateSectionSize(Offset, &SectionSize))
|
||
ptdh->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_STACK;
|
||
ptdh->CallStackOffset = Offset;
|
||
ptdh->SizeOfCallStack = SectionSize;
|
||
ptdh->TopOfStack = (LONG_PTR)StackTop;
|
||
|
||
RtlCopyMemory((char*)pDump + Offset,
|
||
(char*)StackTop,
|
||
SectionSize);
|
||
|
||
Offset += SectionSize;
|
||
}
|
||
|
||
#if defined(_IA64_)
|
||
{
|
||
ULONG_PTR BStoreTop = pContext->RsBSP;
|
||
ULONG_PTR BStoreBase = (ULONG_PTR)pThread->Tcb.InitialBStore;
|
||
ULONG_PTR BStoreLimit = (ULONG_PTR)pThread->Tcb.BStoreLimit;
|
||
|
||
if ((BStoreBase >= BStoreTop) || (BStoreTop > BStoreLimit))
|
||
BStoreTop = (ULONG_PTR)pThread->Tcb.BStoreLimit;
|
||
|
||
SectionSize = (BStoreTop > BStoreBase) ? (ULONG)(BStoreTop - BStoreBase) : 0;
|
||
SectionSize = min(SectionSize, MAX_TRIAGE_STACK_SIZE - 1);
|
||
SectionSize = IopGetMaxValidSectionSizeDown(BStoreTop, SectionSize);
|
||
|
||
if (SectionSize) {
|
||
if (!IopValidateSectionSize(Offset, &SectionSize))
|
||
ptdh->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
|
||
ptdh->ArchitectureSpecific.Ia64.BStoreOffset = Offset;
|
||
ptdh->ArchitectureSpecific.Ia64.SizeOfBStore = SectionSize;
|
||
ptdh->ArchitectureSpecific.Ia64.LimitOfBStore = (LONG_PTR)BStoreTop;
|
||
RtlCopyMemory((char*)pDump + Offset,
|
||
(char*)BStoreTop - SectionSize + 1,
|
||
SectionSize);
|
||
|
||
Offset += SectionSize;
|
||
}
|
||
}
|
||
#endif // defined(_IA64_)
|
||
}
|
||
|
||
//
|
||
// Loaded modules list
|
||
//
|
||
|
||
{
|
||
ULONG DrvOffset = ALIGN_8(Offset);
|
||
ULONG DrvCount, StrDataSize;
|
||
KIRQL OldIrql;
|
||
|
||
OldIrql = KeGetCurrentIrql();
|
||
if (OldIrql < DISPATCH_LEVEL) {
|
||
KeRaiseIrqlToDpcLevel();
|
||
}
|
||
ExAcquireSpinLockAtDpcLevel(&PsLoadedModuleSpinLock);
|
||
|
||
if (NT_SUCCESS(IopGetLoadedDriverInfo(&DrvCount, &StrDataSize))) {
|
||
SectionSize = ALIGN_8(DrvCount * sizeof(DUMP_DRIVER_ENTRY));
|
||
if (SectionSize &&
|
||
IopValidateSectionSize(DrvOffset, &SectionSize))
|
||
{
|
||
ULONG StrOffset = DrvOffset + SectionSize;
|
||
SectionSize = ALIGN_8(StrDataSize +
|
||
DrvCount * (sizeof(WCHAR) +
|
||
sizeof(DUMP_STRING)));
|
||
if (SectionSize &&
|
||
IopValidateSectionSize(StrOffset, &SectionSize))
|
||
{
|
||
if (NT_SUCCESS(IopWriteDriverList((ULONG_PTR)pDump,
|
||
TRIAGE_DUMP_DATA_SIZE,
|
||
DrvOffset,
|
||
StrOffset)))
|
||
{
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_DRIVER_LIST;
|
||
ptdh->DriverListOffset = DrvOffset;
|
||
ptdh->DriverCount = DrvCount;
|
||
ptdh->StringPoolOffset = (ULONG)StrOffset;
|
||
ptdh->StringPoolSize = SectionSize;
|
||
Offset = StrOffset + SectionSize;
|
||
}
|
||
} else {
|
||
ptdh->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
} else {
|
||
ptdh->TriageOptions |= TRIAGE_OPTION_OVERFLOWED;
|
||
}
|
||
}
|
||
|
||
ExReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
|
||
} // Loaded modules list
|
||
|
||
//
|
||
// Save some current code
|
||
//
|
||
|
||
SectionSize = PAGE_SIZE + sizeof(TRIAGE_DATA_BLOCK);
|
||
IopValidateSectionSize(ALIGN_8(Offset), &SectionSize);
|
||
if (SectionSize > sizeof(TRIAGE_DATA_BLOCK)) {
|
||
ULONG DataSize = SectionSize - sizeof(TRIAGE_DATA_BLOCK);
|
||
ULONG PreIpSize = IopGetMaxValidSectionSizeDown(PROGRAM_COUNTER(pContext),
|
||
DataSize / 2);
|
||
|
||
if (PreIpSize) {
|
||
ULONG_PTR CodeStartOffset = PROGRAM_COUNTER(pContext) - PreIpSize + 1;
|
||
DataSize = IopGetMaxValidSectionSize(CodeStartOffset,
|
||
DataSize);
|
||
|
||
if (DataSize) {
|
||
PTRIAGE_DATA_BLOCK pDataBlock;
|
||
|
||
Offset = ALIGN_8(Offset);
|
||
|
||
pdh->MiniDumpFields |= TRIAGE_DUMP_DATA_BLOCKS;
|
||
ptdh->DataBlocksOffset = Offset;
|
||
ptdh->DataBlocksCount = 1;
|
||
|
||
pDataBlock = (PTRIAGE_DATA_BLOCK)((char*)pDump + Offset);
|
||
Offset += sizeof(*pDataBlock);
|
||
Offset = ALIGN_8(Offset);
|
||
|
||
pDataBlock->Address = (LONG_PTR)CodeStartOffset;
|
||
pDataBlock->Size = DataSize;
|
||
pDataBlock->Offset = Offset;
|
||
RtlCopyMemory((char*)pDump + Offset,
|
||
(char*)CodeStartOffset,
|
||
DataSize);
|
||
Offset += DataSize;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// End of dump validation
|
||
//
|
||
|
||
ptdh->ValidOffset = TRIAGE_DUMP_SIZE - sizeof(ULONG);
|
||
*(PULONG)((char*)pDump + ptdh->ValidOffset) = TRIAGE_DUMP_VALID;
|
||
Offset = TRIAGE_DUMP_SIZE;
|
||
return Offset;
|
||
}
|