4269 lines
114 KiB
C
4269 lines
114 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
|
||
|
--*/
|
||
|
|
||
|
#include "iop.h"
|
||
|
#include "ntddft.h"
|
||
|
#include <inbv.h>
|
||
|
#include <windef.h>
|
||
|
|
||
|
// Processor specific macros.
|
||
|
#if defined (i386)
|
||
|
#define PROGRAM_COUNTER(_context) ((_context)->Eip)
|
||
|
#define STACK_POINTER(_context) ((_context)->Esp)
|
||
|
#define CURRENT_IMAGE_TYPE() IMAGE_FILE_MACHINE_I386
|
||
|
#define PaeEnabled() X86PaeEnabled()
|
||
|
#elif defined (ALPHA)
|
||
|
#define PROGRAM_COUNTER(_context) ((_context)->Fir)
|
||
|
#define STACK_POINTER(_context) ((_context)->IntSp)
|
||
|
#define CURRENT_IMAGE_TYPE() IMAGE_FILE_MACHINE_ALPHA
|
||
|
#define PaeEnabled() (FALSE)
|
||
|
#elif defined (_IA64_)
|
||
|
#define PROGRAM_COUNTER(_context) ((_context)->StIIP)
|
||
|
#define STACK_POINTER(_context) ((_context)->IntSp)
|
||
|
#define CURRENT_IMAGE_TYPE() IMAGE_FILE_MACHINE_IA64
|
||
|
#define PaeEnabled() (FALSE)
|
||
|
#else
|
||
|
#error ("unknown processor type")
|
||
|
#endif
|
||
|
|
||
|
// min3(_a,_b,_c)
|
||
|
|
||
|
// Same as min() but takes 3 parameters.
|
||
|
#define min3(_a,_b,_c) ( min ( min ((_a), (_b)), min ((_a), (_c))) )
|
||
|
|
||
|
|
||
|
// Global variables
|
||
|
extern PVOID MmPfnDatabase;
|
||
|
extern PFN_NUMBER MmHighestPossiblePhysicalPage;
|
||
|
|
||
|
NTSTATUS IopFinalCrashDumpStatus = -1;
|
||
|
ULONG IopCrashDumpStateChange = 0;
|
||
|
BOOLEAN IopDumpFileContainsNewDump = 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 MAX_TRIAGE_STACK_SIZE ( 16 * 1024 )
|
||
|
#define DEFAULT_TRIAGE_DUMP_FLAGS (0xFFFFFFFF)
|
||
|
|
||
|
|
||
|
// Function prototypes
|
||
|
|
||
|
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 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
|
||
|
);
|
||
|
|
||
|
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 IoSetCrashDumpState(IN SYSTEM_CRASH_STATE_INFORMATION *pDumpState);
|
||
|
PSUMMARY_DUMP_HEADER IopInitializeSummaryDump(IN PDUMP_CONTROL_BLOCK pDcb);
|
||
|
|
||
|
NTSTATUS
|
||
|
IopWriteSummaryHeader(
|
||
|
IN PSUMMARY_DUMP_HEADER pSummaryHeader,
|
||
|
IN PDUMP_DRIVER_WRITE pfWrite,
|
||
|
IN OUT PLARGE_INTEGER * pMcbBuffer,
|
||
|
IN OUT PMDL pMdl,
|
||
|
IN ULONG dwWriteSize,
|
||
|
IN ULONG dwLength
|
||
|
);
|
||
|
|
||
|
VOID IopMapVirtualToPhysicalMdl(IN OUT PMDL pMdl, IN ULONG_PTR dwMemoryAddress, IN ULONG dwLength);
|
||
|
ULONG IopCreateSummaryDump(IN PSUMMARY_DUMP_HEADER pHeader);
|
||
|
VOID IopDeleteNonExistentMemory(PSUMMARY_DUMP_HEADER pHeader, PPHYSICAL_MEMORY_DESCRIPTOR MmPhysicalMemoryBlock);
|
||
|
|
||
|
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);
|
||
|
|
||
|
#if DBG
|
||
|
VOID IopDebugPrint(ULONG DebugPrintLevel, PCCHAR DebugMessage, ...);
|
||
|
#define IoDebugPrint(X) IopDebugPrint X
|
||
|
#else
|
||
|
#define IoDebugPrint(X)
|
||
|
#endif //DBG
|
||
|
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(PAGE,IoGetDumpStack)
|
||
|
#pragma alloc_text(PAGE,IopLoadDumpDriver)
|
||
|
#pragma alloc_text(PAGE,IoFreeDumpStack)
|
||
|
#pragma alloc_text(PAGE,IoGetCrashDumpInformation)
|
||
|
#pragma alloc_text(PAGE,IoGetCrashDumpStateInformation)
|
||
|
#pragma alloc_text(PAGE,IoSetCrashDumpState)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined (i386)
|
||
|
|
||
|
// Functions
|
||
|
BOOL 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 = COMPUTE_PAGES_SPANNED(VirtualAddress, Length);
|
||
|
|
||
|
while (Pages) {
|
||
|
if (!MmIsAddressValid((LPVOID)Va)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Va += PAGE_SIZE;
|
||
|
Pages--;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopGetDumpStack(
|
||
|
IN PWCHAR ModulePrefix,
|
||
|
OUT PDUMP_STACK_CONTEXT * pDumpStack,
|
||
|
IN PUNICODE_STRING pUniDeviceName,
|
||
|
IN PWCHAR pDumpDriverName,
|
||
|
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
|
||
|
|
||
|
pDeviceName - The name of the target dump device
|
||
|
|
||
|
pDumpDriverName - 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;
|
||
|
PUCHAR PartitionName;
|
||
|
ANSI_STRING AnsiString;
|
||
|
UNICODE_STRING TempName;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
NTSTATUS Status;
|
||
|
HANDLE DeviceHandle;
|
||
|
SCSI_ADDRESS ScsiAddress;
|
||
|
BOOLEAN ScsiDump;
|
||
|
PARTITION_INFORMATION PartitionInfo;
|
||
|
PFILE_OBJECT FileObject;
|
||
|
PDEVICE_OBJECT DeviceObject;
|
||
|
PINITIALIZATION_CONTEXT DumpInit;
|
||
|
PDUMP_POINTERS DumpPointers;
|
||
|
UNICODE_STRING DriverName;
|
||
|
PDRIVER_OBJECT DriverObject;
|
||
|
PIRP Irp;
|
||
|
PIO_STACK_LOCATION IrpSp;
|
||
|
IO_STATUS_BLOCK IoStatus;
|
||
|
PWCHAR DumpName, NameOffset;
|
||
|
KEVENT Event;
|
||
|
PVOID p1;
|
||
|
PHYSICAL_ADDRESS pa;
|
||
|
ULONG i;
|
||
|
IO_STACK_LOCATION irpSp;
|
||
|
ULONG information;
|
||
|
|
||
|
IoDebugPrint((2, "IopGetDumpStack: Prefix:%ws stk: %x device:%ws driver:%ws\n",
|
||
|
ModulePrefix, pDumpStack, pUniDeviceName->Buffer, pDumpDriverName));
|
||
|
|
||
|
ASSERT(DeviceUsageTypeUndefined != UsageType);
|
||
|
|
||
|
DumpStack = ExAllocatePoolWithTag(
|
||
|
NonPagedPool,
|
||
|
sizeof(DUMP_STACK_CONTEXT) + sizeof(DUMP_POINTERS),
|
||
|
'pmuD'
|
||
|
);
|
||
|
|
||
|
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 = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, 'pmuD');
|
||
|
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,
|
||
|
pUniDeviceName,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
Status = ZwOpenFile(
|
||
|
&DeviceHandle,
|
||
|
FILE_READ_DATA | SYNCHRONIZE,
|
||
|
&ObjectAttributes,
|
||
|
&IoStatus,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
FILE_NON_DIRECTORY_FILE
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
IoDebugPrint((0, "IODUMP: 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 = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_ADDRESS), 'pmuD');
|
||
|
|
||
|
// It is ok If the allocation fails. The scsi dump driver will scan
|
||
|
// all devices if the targetaddress information does not exist
|
||
|
|
||
|
|
||
|
if (DumpInit->TargetAddress) {
|
||
|
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,
|
||
|
NULL,
|
||
|
0,
|
||
|
&PartitionInfo,
|
||
|
sizeof(PARTITION_INFORMATION)
|
||
|
);
|
||
|
|
||
|
if (Status == STATUS_PENDING) {
|
||
|
ZwWaitForSingleObject(DeviceHandle, FALSE, NULL);
|
||
|
|
||
|
Status = IoStatus.Status;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "Partition Type = %x\n", PartitionInfo.PartitionType));
|
||
|
IoDebugPrint((2, "Boot Indicator = %x\n", PartitionInfo.BootIndicator));
|
||
|
|
||
|
Status = ZwDeviceIoControlFile(
|
||
|
DeviceHandle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatus,
|
||
|
IOCTL_DISK_GET_DRIVE_LAYOUT,
|
||
|
NULL,
|
||
|
0,
|
||
|
Buffer,
|
||
|
PAGE_SIZE
|
||
|
);
|
||
|
|
||
|
if (Status == STATUS_PENDING) {
|
||
|
ZwWaitForSingleObject(DeviceHandle, FALSE, NULL);
|
||
|
Status = IoStatus.Status;
|
||
|
}
|
||
|
|
||
|
DumpInit->DiskSignature = ((PDRIVE_LAYOUT_INFORMATION)Buffer)->Signature;
|
||
|
|
||
|
|
||
|
// 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)) {
|
||
|
|
||
|
IoDebugPrint((0,
|
||
|
"IODUMP: Could not get dump pointers; error = %x, length %x\n",
|
||
|
Status,
|
||
|
IoStatus.Information
|
||
|
));
|
||
|
ObDereferenceObject(FileObject);
|
||
|
// NtClose (DeviceHandle);
|
||
|
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(INITIALIZATION_CONTEXT);
|
||
|
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) {
|
||
|
|
||
|
#if !defined(NO_LEGACY_DRIVERS)
|
||
|
p1 = HalAllocateCommonBuffer(DumpInit->AdapterObject, DumpPointers->CommonBufferSize, &pa, FALSE);
|
||
|
|
||
|
#else
|
||
|
p1 = (*((PDMA_ADAPTER)DumpInit->AdapterObject)->DmaOperations->
|
||
|
AllocateCommonBuffer)((PDMA_ADAPTER)DumpInit->AdapterObject, DumpPointers->CommonBufferSize, &pa, FALSE);
|
||
|
#endif // NO_LEGACY_DRIVERS
|
||
|
}
|
||
|
else {
|
||
|
p1 = MmAllocateContiguousMemory(DumpPointers->CommonBufferSize, pa);
|
||
|
if (!p1) {
|
||
|
p1 = MmAllocateNonCachedMemory(DumpPointers->CommonBufferSize);
|
||
|
}
|
||
|
pa = MmGetPhysicalAddress(p1);
|
||
|
}
|
||
|
|
||
|
if (!p1) {
|
||
|
IoDebugPrint((0, "IODUMP: 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, pDumpDriverName, 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)) {
|
||
|
IoDebugPrint((0,
|
||
|
"IODUMP: 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, (VOID **)&information);
|
||
|
ASSERT(0 == information);
|
||
|
|
||
|
if (!NT_SUCCESS(Status) && IgnoreDeviceUsageFailure) {
|
||
|
IoDebugPrint((0,
|
||
|
"IopGetDumpStack: DEVICE_USAGE_NOTIFICATION "
|
||
|
"Error ignored (%x)\n",
|
||
|
Status));
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
if (NT_SUCCESS(Status)) {
|
||
|
DumpStack->UsageType = UsageType;
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
if (NT_SUCCESS(Status)) {
|
||
|
*pDumpStack = 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;
|
||
|
PLDR_DATA_TABLE_ENTRY ImageLdrInfo;
|
||
|
UNICODE_STRING DriverName;
|
||
|
UNICODE_STRING BaseName;
|
||
|
UNICODE_STRING Prefix;
|
||
|
PUNICODE_STRING LoadBaseName;
|
||
|
|
||
|
// Allocate space to track this dump driver
|
||
|
DumpImage = ExAllocatePoolWithTag(NonPagedPool, sizeof(DUMP_STACK_IMAGE), 'pmuD');
|
||
|
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 = ExAllocatePoolWithTag(NonPagedPool, BaseName.MaximumLength, 'pmuD');
|
||
|
if (!BaseName.Buffer) {
|
||
|
ExFreePool(DumpImage);
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
|
||
|
BaseName.Length = 0;
|
||
|
RtlAppendUnicodeStringToString(&BaseName, &Prefix);
|
||
|
RtlAppendUnicodeToString(&BaseName, NewBaseNameString);
|
||
|
}
|
||
|
|
||
|
Status = MmLoadAndLockSystemImage(&DriverName, &Prefix, LoadBaseName, &DumpImage->Image, &DumpImage->ImageBase);
|
||
|
if (NewBaseNameString) {
|
||
|
ExFreePool(BaseName.Buffer);
|
||
|
}
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
IoDebugPrint((0, "IODUMP: 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->MemoryDescriptor, Dcb->MemoryDescriptorLength);
|
||
|
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));
|
||
|
Check = PoSimpleCheck(Check, DumpImage->ImageBase, DumpImage->SizeOfImage);
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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
|
||
|
--*/
|
||
|
{
|
||
|
PINITIALIZATION_CONTEXT DumpInit;
|
||
|
PLIST_ENTRY Link;
|
||
|
ULONG Check;
|
||
|
NTSTATUS Status;
|
||
|
PDRIVER_INITIALIZE DriverInit;
|
||
|
PDUMP_STACK_IMAGE DumpImage;
|
||
|
|
||
|
|
||
|
DumpInit = &DumpStack->Init;
|
||
|
|
||
|
|
||
|
// Verify checksum on DumpStack structure
|
||
|
|
||
|
|
||
|
// BUGBUG: later
|
||
|
|
||
|
|
||
|
// 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 = DumpImage->Image->EntryPoint;
|
||
|
Status = DriverInit(NULL, (PUNICODE_STRING)DumpInit);
|
||
|
DumpInit = NULL;
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
IoDebugPrint((0, "IODUMP: Unable to initialize driver; error = %x\n", Status));
|
||
|
return Status;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DumpInit = &DumpStack->Init;
|
||
|
|
||
|
|
||
|
// Display string we are starting
|
||
|
|
||
|
|
||
|
if (MessageBuffer) {
|
||
|
InbvDisplayString(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)) {
|
||
|
IoDebugPrint((0, "IODUMP: 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
|
||
|
--*/
|
||
|
{
|
||
|
PINITIALIZATION_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;
|
||
|
ULONG information;
|
||
|
|
||
|
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, (VOID **)&information);
|
||
|
ASSERT(0 == information);
|
||
|
}
|
||
|
else {
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Free any common buffers which where allocated
|
||
|
|
||
|
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
if (DumpInit->CommonBuffer[i]) {
|
||
|
if (DumpInit->AdapterObject) {
|
||
|
|
||
|
#if !defined(NO_LEGACY_DRIVERS)
|
||
|
HalFreeCommonBuffer(
|
||
|
DumpInit->AdapterObject,
|
||
|
((PDUMP_POINTERS)DumpStack->DumpPointers)->CommonBufferSize,
|
||
|
DumpInit->PhysicalAddress[i],
|
||
|
DumpInit->CommonBuffer[i],
|
||
|
FALSE
|
||
|
);
|
||
|
#else
|
||
|
(*((PDMA_ADAPTER)DumpInit->AdapterObject)->DmaOperations->
|
||
|
FreeCommonBuffer)(
|
||
|
(PDMA_ADAPTER)DumpInit->AdapterObject,
|
||
|
((PDUMP_POINTERS)DumpStack->DumpPointers)->CommonBufferSize,
|
||
|
DumpInit->PhysicalAddress[i],
|
||
|
DumpInit->CommonBuffer[i],
|
||
|
FALSE
|
||
|
);
|
||
|
|
||
|
#endif // NO_LEGACY_DRIVERS
|
||
|
|
||
|
|
||
|
}
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopInitializeDumpSpaceAndType(
|
||
|
IN PDUMP_CONTROL_BLOCK dcb,
|
||
|
IN OUT PULONG block,
|
||
|
IN PSUMMARY_DUMP_HEADER pSummaryHeader
|
||
|
)
|
||
|
{
|
||
|
LARGE_INTEGER Space;
|
||
|
|
||
|
Space.QuadPart = 0;
|
||
|
|
||
|
if (dcb->Flags & DCB_TRIAGE_DUMP_ENABLED) {
|
||
|
|
||
|
|
||
|
// Fixed size dump for triage-dumps.
|
||
|
|
||
|
|
||
|
block[DH_DUMP_TYPE] = DUMP_TYPE_TRIAGE;
|
||
|
Space.QuadPart = TRIAGE_DUMP_SIZE;
|
||
|
|
||
|
|
||
|
}
|
||
|
else if (dcb->Flags & DCB_SUMMARY_DUMP_ENABLED) {
|
||
|
|
||
|
block[DH_DUMP_TYPE] = DUMP_TYPE_SUMMARY;
|
||
|
|
||
|
Space = IopCalculateRequiredDumpSpace(
|
||
|
dcb->Flags,
|
||
|
dcb->HeaderSize,
|
||
|
dcb->MemoryDescriptor->NumberOfPages,
|
||
|
pSummaryHeader->Pages
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
if (dcb->Flags & DCB_DUMP_HEADER_ENABLED) {
|
||
|
block[DH_DUMP_TYPE] = DUMP_TYPE_HEADER;
|
||
|
}
|
||
|
|
||
|
Space = IopCalculateRequiredDumpSpace(
|
||
|
dcb->Flags,
|
||
|
dcb->HeaderSize,
|
||
|
dcb->MemoryDescriptor->NumberOfPages,
|
||
|
dcb->MemoryDescriptor->NumberOfPages
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
block[DH_REQUIRED_DUMP_SPACE] = Space.LowPart;
|
||
|
block[DH_REQUIRED_DUMP_SPACE + 1] = Space.HighPart;
|
||
|
|
||
|
IoDebugPrint((2, "IODUMP: dcb File Size %x Block Size %x\n",
|
||
|
block[DH_REQUIRED_DUMP_SPACE],
|
||
|
(ULONG)dcb->DumpFileSize.LowPart
|
||
|
));
|
||
|
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
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.
|
||
|
--*/
|
||
|
{
|
||
|
PDUMP_CONTROL_BLOCK dcb;
|
||
|
PDUMP_STACK_CONTEXT dumpStack;
|
||
|
PDUMP_DRIVER_WRITE write;
|
||
|
PDUMP_DRIVER_FINISH finishUp;
|
||
|
PDUMP_HEADER header;
|
||
|
EXCEPTION_RECORD exception;
|
||
|
PCONTEXT context = ContextSave;
|
||
|
PULONG block;
|
||
|
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;
|
||
|
NTSTATUS status;
|
||
|
UCHAR messageBuffer[128];
|
||
|
PFN_NUMBER ActualPages;
|
||
|
PSUMMARY_DUMP_HEADER pSummaryHeader;
|
||
|
ULONG dwTransferSize;
|
||
|
LARGE_INTEGER requiredDumpSpace;
|
||
|
ULONG_PTR DirBasePage;
|
||
|
|
||
|
// Begin by determining whether or not crash dumps are enabled.
|
||
|
// If not, check to see whether or not auto-rebooting is 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.
|
||
|
if (IopGetDumpControlBlockCheck(dcb) != IopDumpControlBlockChecksum) {
|
||
|
IoDebugPrint((0, "CRASHDUMP: Disk dump routine returning due to DCB integrity error\n"
|
||
|
" No dump will be created\n"));
|
||
|
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Message that we are starting the crashdump
|
||
|
|
||
|
|
||
|
dumpStack = dcb->DumpStack;
|
||
|
sprintf(messageBuffer, "%Z\n", &dumpStack->InitMsg);
|
||
|
|
||
|
|
||
|
// Initialize the dump stack
|
||
|
|
||
|
|
||
|
status = IoInitializeDumpStack(dumpStack, messageBuffer);
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "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.
|
||
|
|
||
|
|
||
|
block = dcb->HeaderPage;
|
||
|
header = (PDUMP_HEADER)block;
|
||
|
|
||
|
RtlFillMemoryUlong(header, PAGE_SIZE, 'EGAP');
|
||
|
header->ValidDump = 'PMUD';
|
||
|
header->BugCheckCode = BugCheckCode;
|
||
|
header->BugCheckParameter1 = BugCheckParameter1;
|
||
|
header->BugCheckParameter2 = BugCheckParameter2;
|
||
|
header->BugCheckParameter3 = BugCheckParameter3;
|
||
|
header->BugCheckParameter4 = BugCheckParameter4;
|
||
|
#if defined (i386)
|
||
|
|
||
|
// 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;
|
||
|
#else
|
||
|
header->DirectoryTableBase = KeGetCurrentThread()->ApcState.Process->DirectoryTableBase[0];
|
||
|
#endif
|
||
|
header->PfnDataBase = MmPfnDatabase;
|
||
|
header->PsLoadedModuleList = &PsLoadedModuleList;
|
||
|
header->PsActiveProcessHead = &PsActiveProcessHead;
|
||
|
header->NumberProcessors = dcb->NumberProcessors;
|
||
|
header->MajorVersion = dcb->MajorVersion;
|
||
|
header->MinorVersion = dcb->MinorVersion;
|
||
|
header->KdDebuggerDataBlock = KdGetDataBlock();
|
||
|
header->PaeEnabled = PaeEnabled();
|
||
|
|
||
|
header->MachineImageType = CURRENT_IMAGE_TYPE();
|
||
|
|
||
|
if (!(dcb->Flags & DCB_DUMP_ENABLED)) {
|
||
|
dcb->MemoryDescriptor->NumberOfPages = 1;
|
||
|
}
|
||
|
|
||
|
strcpy(header->VersionUser, dcb->VersionUser);
|
||
|
|
||
|
RtlCopyMemory(&block[DH_PHYSICAL_MEMORY_BLOCK],
|
||
|
dcb->MemoryDescriptor,
|
||
|
sizeof(PHYSICAL_MEMORY_DESCRIPTOR) + ((dcb->MemoryDescriptor->NumberOfRuns - 1) * sizeof(PHYSICAL_MEMORY_RUN)));
|
||
|
RtlCopyMemory(&block[DH_CONTEXT_RECORD], context, sizeof(CONTEXT));
|
||
|
|
||
|
exception.ExceptionCode = STATUS_BREAKPOINT;
|
||
|
exception.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
|
||
|
exception.NumberParameters = 0;
|
||
|
exception.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
||
|
exception.ExceptionAddress = (PVOID)PROGRAM_COUNTER(context);
|
||
|
|
||
|
RtlCopyMemory(&block[DH_EXCEPTION_RECORD], &exception, sizeof(EXCEPTION_RECORD));
|
||
|
|
||
|
block[DH_DUMP_TYPE] = DUMP_TYPE_FULL;// Init dump type to FULL
|
||
|
|
||
|
// Set the timestamp
|
||
|
#ifdef _WIN64
|
||
|
RtlCopyMemory(&block[DH_CRASH_DUMP_TIMESTAMP], (PCHAR)(&SharedUserData->SystemLowTime), sizeof(LARGE_INTEGER));
|
||
|
#else
|
||
|
RtlCopyMemory(&block[DH_CRASH_DUMP_TIMESTAMP], (PCHAR)(&SharedUserData->SystemTime), sizeof(LARGE_INTEGER));
|
||
|
#endif
|
||
|
|
||
|
// 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.
|
||
|
RtlZeroMemory(&block[DH_REQUIRED_DUMP_SPACE], sizeof(LARGE_INTEGER));
|
||
|
|
||
|
if (dcb->Flags & DCB_DUMP_ENABLED) {
|
||
|
if ((dcb->Flags & DCB_SUMMARY_DUMP_ENABLED)) {// If summary dump try to create the dump header
|
||
|
pSummaryHeader = IopInitializeSummaryDump(dcb);// Initialize the summary dump
|
||
|
if (!pSummaryHeader) {// No summary dump header so return.
|
||
|
IoDebugPrint((1, "IoWriteCrashDump: Error Null summary dump header\n"));
|
||
|
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IopInitializeDumpSpaceAndType(dcb, block, pSummaryHeader);
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
// All of the pieces of the header file have been generated. Write
|
||
|
// the header page to the paging file, using the appropriate drivers, etc.
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: 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.
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: Header Page written\n"));
|
||
|
|
||
|
// If only requesting a header dump, we are now done.
|
||
|
if (dcb->Flags & DCB_DUMP_HEADER_ENABLED) {
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: Only Dumping Dump Header\n"));
|
||
|
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,
|
||
|
dcb->TriageDumpBuffer,
|
||
|
dcb->TriageDumpBufferSize - PAGE_SIZE,
|
||
|
dcb->BuildNumber,
|
||
|
dcb->Flags
|
||
|
);
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
IoDebugPrint((1, "IoWriteCrashDump: Failed to write triage-dump\n"));
|
||
|
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((1, "IoWriteCrashDump: Successfully wrote triage-dump\n"));
|
||
|
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) {
|
||
|
PFN_NUMBER pagesDoneSoFar = 0;
|
||
|
ULONG currentPercentage = 0;
|
||
|
ULONG maximumPercentage = 0;
|
||
|
|
||
|
// Actual Pages is the number of pages to dump.
|
||
|
ActualPages = dcb->MemoryDescriptor->NumberOfPages;
|
||
|
|
||
|
if (dcb->Flags & DCB_SUMMARY_DUMP_ENABLED) {
|
||
|
PRTL_BITMAP PageMap;
|
||
|
|
||
|
ASSERT(pSummaryHeader != NULL);
|
||
|
|
||
|
PageMap = (PRTL_BITMAP)(pSummaryHeader + 1);
|
||
|
ASSERT(PageMap != NULL);
|
||
|
|
||
|
// At this point the dump header header has been sucessfully written. Write the summary dump header.
|
||
|
status = IopWriteSummaryHeader(pSummaryHeader, write, &mcb, mdl, dwTransferSize, (dcb->HeaderSize - PAGE_SIZE));
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
IoDebugPrint((1, "IoWriteCrashDump: Error writing summary dump header\n"));
|
||
|
IopFinalCrashDumpStatus = status;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: Sucessfully wrote summary dump header\n"));
|
||
|
ActualPages = pSummaryHeader->Pages;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: Writing Memory Dump\n"));
|
||
|
|
||
|
// Set the virtual file offset and initialize loop variables and constants.
|
||
|
memoryAddress = (ULONG_PTR)dcb->MemoryDescriptor->Run[0].BasePage * PAGE_SIZE;
|
||
|
if (dcb->Flags & DCB_SUMMARY_DUMP_ENABLED) {
|
||
|
PRTL_BITMAP BitMap;
|
||
|
|
||
|
BitMap = (PRTL_BITMAP)(pSummaryHeader + 1);
|
||
|
|
||
|
status = IopWriteSummaryDump(BitMap, write, &dumpStack->ProgMsg, messageBuffer, mcb, dwTransferSize);
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
IoDebugPrint((1, "IoWriteCrashDump: Failed to write triage-dump\n"));
|
||
|
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((1, "IoWriteCrashDump: Successfully wrote triage-dump\n"));
|
||
|
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;
|
||
|
}
|
||
|
pagesDoneSoFar += byteCount / PAGE_SIZE;
|
||
|
|
||
|
currentPercentage = (ULONG)((pagesDoneSoFar * 100) / ActualPages);
|
||
|
if (currentPercentage > maximumPercentage) {
|
||
|
maximumPercentage = currentPercentage;
|
||
|
// Update message on screen.
|
||
|
sprintf(messageBuffer, "%Z: %3d\r", &dumpStack->ProgMsg, maximumPercentage);
|
||
|
InbvDisplayString(messageBuffer);
|
||
|
}
|
||
|
|
||
|
// Map the physical memory and write it to the current segment of the file.
|
||
|
IopMapPhysicalMemory(mdl, memoryAddress, &dcb->MemoryDescriptor->Run[0], byteCount);
|
||
|
|
||
|
// Write the next segment.
|
||
|
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 (pagesDoneSoFar >= ActualPages) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: memory dump written\n"));
|
||
|
}
|
||
|
|
||
|
FinishDump:
|
||
|
sprintf(messageBuffer, "%Z", &dumpStack->DoneMsg);
|
||
|
InbvDisplayString(messageBuffer);
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// Check to see whether or not auto-reboots are enabled and, if so, reboot now.
|
||
|
if (dcb->Flags & DCB_AUTO_REBOOT) {
|
||
|
IoDebugPrint((0, "IODUMP: Autorebooting\n"));
|
||
|
KeReturnToFirmware(HalRebootRoutine);
|
||
|
}
|
||
|
|
||
|
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 = COMPUTE_PAGES_SPANNED(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 dwMaxPage, IN PRTL_BITMAP pBitMapHeader, IN ULONG dwPageFrameIndex, IN ULONG dwNumberOfPages)
|
||
|
{
|
||
|
// Sometimes we get PFNs that are out of range. Just ignore them.
|
||
|
if (dwPageFrameIndex >= dwMaxPage) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RtlSetBits(pBitMapHeader, dwPageFrameIndex, dwNumberOfPages);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID IopRemovePageFromPageMap(IN ULONG dwMaxPage, IN PRTL_BITMAP pBitMapHeader, IN ULONG dwPageFrameIndex, IN ULONG dwNumberOfPages)
|
||
|
{
|
||
|
// Sometimes we get PFNs that are out of range. Just ignore them.
|
||
|
if (dwPageFrameIndex >= dwMaxPage) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((6, "RtlClearBits max:%x pfn:%x, pages:%x\n", (dwMaxPage), (dwPageFrameIndex), (dwNumberOfPages))); \
|
||
|
RtlClearBits(pBitMapHeader, dwPageFrameIndex, dwNumberOfPages);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTKERNELAPI NTSTATUS IoGetCrashDumpInformation(OUT PSYSTEM_CRASH_DUMP_INFORMATION pCrashDumpInfo)
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
This function checks to see if an open crash dump section exists and if so creates a handle to the section and returns that value in the CrashDumpInformation structure.
|
||
|
Arguments:
|
||
|
CrashInfo - Supplies a pointer to the crash dump information structure.
|
||
|
Return Value:
|
||
|
Status of the operation. A handle value of zero indicates no crash dump was located.
|
||
|
--*/
|
||
|
{
|
||
|
if (!IopDumpControlBlock) {
|
||
|
return STATUS_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
// NB: The section object is for direct dump support, which has been removed.
|
||
|
pCrashDumpInfo->hDumpSection = NULL;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTKERNELAPI NTSTATUS IoGetCrashDumpStateInformation(OUT PSYSTEM_CRASH_STATE_INFORMATION pCrashDumpState)
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
This routine returns sets the state variable for direct dump (fast dump)
|
||
|
Arguments:
|
||
|
CrashInfo - Supplies a pointer to the crash dump state information structure.
|
||
|
Return Value:
|
||
|
Status of the operation. A handle value of zero indicates no crash dump was located.
|
||
|
--*/
|
||
|
{
|
||
|
pCrashDumpState->ValidDirectDump = FALSE;
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IoSetDumpRange(
|
||
|
IN PVOID DumpContext,
|
||
|
IN PVOID StartVa,
|
||
|
IN ULONG_PTR Pages,
|
||
|
IN BOOLEAN IsPhysicalAddress
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine includes this range of memory in the dump
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DumpContext - dump context
|
||
|
|
||
|
StartVa - Starting VA
|
||
|
|
||
|
Pages - The number of pages to include
|
||
|
|
||
|
IsPhysicalAddress - true if direct physical address translation
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_SUCCESS - On success.
|
||
|
|
||
|
NTSTATUS - Error.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PCHAR Va;
|
||
|
PRTL_BITMAP pBitMapHeader;
|
||
|
PHYSICAL_ADDRESS phyAddr;
|
||
|
PSUMMARY_DUMP_HEADER pHeader;
|
||
|
BOOLEAN AllPagesSet;
|
||
|
|
||
|
|
||
|
Va = StartVa;
|
||
|
pHeader = (PSUMMARY_DUMP_HEADER)DumpContext;
|
||
|
pBitMapHeader = (PRTL_BITMAP)(pHeader + 1);
|
||
|
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);
|
||
|
|
||
|
|
||
|
// IsPhysicalAddress indicates that the va is virtually / physically
|
||
|
// contiguous.
|
||
|
|
||
|
|
||
|
if (IsPhysicalAddress) {
|
||
|
|
||
|
phyAddr = MmGetPhysicalAddress(Va);
|
||
|
IopAddPageToPageMap(pHeader->BitmapSize,
|
||
|
pBitMapHeader,
|
||
|
(ULONG)(phyAddr.QuadPart >> PAGE_SHIFT),
|
||
|
(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(pHeader->BitmapSize,
|
||
|
pBitMapHeader,
|
||
|
(ULONG)(phyAddr.QuadPart >> PAGE_SHIFT),
|
||
|
1);
|
||
|
|
||
|
if (phyAddr.QuadPart >> PAGE_SHIFT > pHeader->BitmapSize) {
|
||
|
AllPagesSet = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Va += PAGE_SIZE;
|
||
|
Pages--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (AllPagesSet) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return STATUS_INVALID_ADDRESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IoFreeDumpRange(
|
||
|
IN PVOID DumpContext,
|
||
|
IN PVOID StartVa,
|
||
|
IN ULONG_PTR Pages,
|
||
|
IN BOOLEAN IsPhysicalAddress
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
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
|
||
|
|
||
|
IsPhysicalAddress - true if direct physical address translation
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_SUCCESS - On success.
|
||
|
|
||
|
NTSTATUS - Error.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PCHAR Va;
|
||
|
PRTL_BITMAP pBitMapHeader;
|
||
|
PHYSICAL_ADDRESS phyAddr;
|
||
|
PSUMMARY_DUMP_HEADER pHeader;
|
||
|
|
||
|
|
||
|
// Round to page size.
|
||
|
|
||
|
|
||
|
Va = StartVa;
|
||
|
pHeader = (PSUMMARY_DUMP_HEADER)DumpContext;
|
||
|
pBitMapHeader = (PRTL_BITMAP)(pHeader + 1);
|
||
|
|
||
|
|
||
|
// 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 (IsPhysicalAddress) {
|
||
|
|
||
|
phyAddr = MmGetPhysicalAddress(Va);
|
||
|
|
||
|
IopRemovePageFromPageMap(pHeader->BitmapSize,
|
||
|
pBitMapHeader,
|
||
|
(ULONG)(phyAddr.QuadPart >> PAGE_SHIFT),
|
||
|
(ULONG)Pages
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
while (Pages) {
|
||
|
|
||
|
|
||
|
// Only do a translation for valid pages.
|
||
|
|
||
|
|
||
|
if (MmIsAddressValid(Va)) {
|
||
|
phyAddr = MmGetPhysicalAddress(Va);
|
||
|
|
||
|
IoDebugPrint((3, "IoFreeDumpRange:Va: %x Pages: %x IsPhysical: %x phyAddr %I64x\n",
|
||
|
StartVa, Pages, IsPhysicalAddress, phyAddr.QuadPart));
|
||
|
|
||
|
IopRemovePageFromPageMap(pHeader->BitmapSize,
|
||
|
pBitMapHeader,
|
||
|
(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 summaryHeaderSize;
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Calculate the summary header size. Includes the header plus bitmap
|
||
|
|
||
|
summaryHeaderSize = (ULONG)ROUND_TO_PAGES(
|
||
|
sizeof(SUMMARY_DUMP_HEADER) +
|
||
|
sizeof(RTL_BITMAP) +
|
||
|
(((maxMemorySize.QuadPart >> PAGE_SHIFT) >> 5) << 2) +
|
||
|
dwHeaderSize
|
||
|
);
|
||
|
|
||
|
maxMemorySize.QuadPart += summaryHeaderSize;
|
||
|
|
||
|
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;
|
||
|
PLDR_DATA_TABLE_ENTRY DriverEntry;
|
||
|
|
||
|
|
||
|
NextEntry = PsLoadedModuleList.Flink;
|
||
|
while (NextEntry != &PsLoadedModuleList) {
|
||
|
|
||
|
DriverEntry = CONTAINING_RECORD(NextEntry,
|
||
|
LDR_DATA_TABLE_ENTRY,
|
||
|
InLoadOrderLinks
|
||
|
);
|
||
|
|
||
|
if (!IopIsAddressRangeValid(DriverEntry, sizeof(*DriverEntry)) ||
|
||
|
!IopIsAddressRangeValid(DriverEntry->BaseDllName.Buffer,
|
||
|
DriverEntry->BaseDllName.Length)) {
|
||
|
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
DriverCount++;
|
||
|
SizeOfStringData += DriverEntry->FullDllName.Length;
|
||
|
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)
|
||
|
|
||
|
|
||
|
#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;
|
||
|
PLDR_DATA_TABLE_ENTRY DriverEntry;
|
||
|
PDUMP_DRIVER_ENTRY DumpImageArray;
|
||
|
PDUMP_STRING DumpStringName = NULL;
|
||
|
PIMAGE_NT_HEADERS NtHeaders;
|
||
|
|
||
|
ASSERT(DriverListOffset != 0);
|
||
|
ASSERT(StringPoolOffset != 0);
|
||
|
|
||
|
|
||
|
DumpImageArray = (PDUMP_DRIVER_ENTRY)(BufferAddress + DriverListOffset);
|
||
|
DumpStringName = (PDUMP_STRING)(BufferAddress + StringPoolOffset);
|
||
|
|
||
|
NextEntry = PsLoadedModuleList.Flink;
|
||
|
|
||
|
while (NextEntry != &PsLoadedModuleList) {
|
||
|
|
||
|
DriverEntry = CONTAINING_RECORD(NextEntry,
|
||
|
LDR_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 (MmDbgReadCheck(DriverEntry->DllBase) != NULL) {
|
||
|
|
||
|
NtHeaders = RtlImageNtHeader(DriverEntry->DllBase);
|
||
|
ASSERT(NtHeaders);
|
||
|
DumpImageArray[i].LdrEntry.TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
|
||
|
DumpImageArray[i].LdrEntry.SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
|
||
|
}
|
||
|
|
||
|
DumpImageArray[i].DriverNameOffset =
|
||
|
(ULONG)((ULONG_PTR)DumpStringName - BufferAddress);
|
||
|
|
||
|
i++;
|
||
|
DumpStringName = DmpNextPoolString(DumpStringName);
|
||
|
NextEntry = NextEntry->Flink;
|
||
|
}
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
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 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_PTR Offset;
|
||
|
ULONG_PTR StartOfStackRegion = 0;
|
||
|
PTRIAGE_DUMP_HEADER TriageDumpHeader = NULL;
|
||
|
PLDR_DATA_TABLE_ENTRY DriverEntry = NULL;
|
||
|
PDUMP_DRIVER_ENTRY DumpImageArray = NULL;
|
||
|
PDUMP_STRING DumpStringName = NULL;
|
||
|
PETHREAD Thread = NULL;
|
||
|
|
||
|
IoDebugPrint((0, "[IopWriteTriageDump] BufferSize = %#x ServicePackBuild = %d\n",
|
||
|
BufferSize,
|
||
|
ServicePackBuild));
|
||
|
|
||
|
|
||
|
// Setup the triage-dump header.
|
||
|
|
||
|
|
||
|
if (BufferSize < sizeof(TRIAGE_DUMP_HEADER) + sizeof(DWORD)) {
|
||
|
return STATUS_NO_MEMORY;
|
||
|
}
|
||
|
|
||
|
TriageDumpHeader = (PTRIAGE_DUMP_HEADER)Buffer;
|
||
|
RtlZeroMemory(TriageDumpHeader, sizeof(*TriageDumpHeader));
|
||
|
|
||
|
|
||
|
// The normal dump header is of size PAGE_SIZE.
|
||
|
|
||
|
|
||
|
TriageDumpHeader->SizeOfDump = BufferSize + PAGE_SIZE;
|
||
|
|
||
|
|
||
|
// Adjust the BufferSize so we can write the final status DWORD at the
|
||
|
// end.
|
||
|
|
||
|
|
||
|
BufferSize -= sizeof(DWORD);
|
||
|
RtlZeroMemory(IndexByByte(Buffer, BufferSize), sizeof(DWORD));
|
||
|
|
||
|
TriageDumpHeader->ValidOffset = (TriageDumpHeader->SizeOfDump - sizeof(ULONG));
|
||
|
TriageDumpHeader->ContextOffset = DH_CONTEXT_RECORD * sizeof(ULONG);
|
||
|
TriageDumpHeader->ExceptionOffset = DH_EXCEPTION_RECORD * sizeof(ULONG);
|
||
|
TriageDumpHeader->BrokenDriverOffset = 0;
|
||
|
TriageDumpHeader->ServicePackBuild = ServicePackBuild;
|
||
|
TriageDumpHeader->TriageOptions = TriageOptions;
|
||
|
|
||
|
Offset = ALIGN_8(PAGE_SIZE + sizeof(TRIAGE_DUMP_HEADER));
|
||
|
|
||
|
|
||
|
// Set the Mm Offset, if necessary.
|
||
|
|
||
|
|
||
|
SizeOfSection = ALIGN_8(MmSizeOfTriageInformation());
|
||
|
|
||
|
if (Offset + SizeOfSection < BufferSize) {
|
||
|
TriageDumpHeader->MmOffset = (ULONG)Offset;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the Unloaded Drivers Offset, if necessary.
|
||
|
|
||
|
|
||
|
SizeOfSection = ALIGN_8(MmSizeOfUnloadedDriverInformation());
|
||
|
|
||
|
if (Offset + SizeOfSection < BufferSize) {
|
||
|
TriageDumpHeader->UnloadedDriversOffset = (ULONG)Offset;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the Prcb Offset, if necessary.
|
||
|
|
||
|
|
||
|
if (Fields & TRIAGE_DUMP_PRCB) {
|
||
|
SizeOfSection = ALIGN_8(sizeof(KPRCB));
|
||
|
|
||
|
if (Offset + SizeOfSection < BufferSize) {
|
||
|
TriageDumpHeader->PrcbOffset = (ULONG)Offset;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the Process Offset, if necessary.
|
||
|
|
||
|
|
||
|
if (Fields & TRIAGE_DUMP_PROCESS) {
|
||
|
SizeOfSection = ALIGN_8(sizeof(EPROCESS));
|
||
|
|
||
|
if (Offset + SizeOfSection < BufferSize) {
|
||
|
TriageDumpHeader->ProcessOffset = (ULONG)Offset;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the Thread Offset, if necessary.
|
||
|
|
||
|
|
||
|
if (Fields & TRIAGE_DUMP_THREAD) {
|
||
|
SizeOfSection = ALIGN_8(sizeof(ETHREAD));
|
||
|
|
||
|
if (Offset + SizeOfSection < BufferSize) {
|
||
|
TriageDumpHeader->ThreadOffset = (ULONG)Offset;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the CallStack Offset, if necessary.
|
||
|
|
||
|
|
||
|
Thread = PsGetCurrentThread();
|
||
|
|
||
|
if (Fields & TRIAGE_DUMP_STACK) {
|
||
|
|
||
|
|
||
|
// If there is a stack, calculate its size.
|
||
|
|
||
|
|
||
|
if (Thread->Tcb.KernelStackResident) {
|
||
|
|
||
|
ULONG_PTR StackBase;
|
||
|
|
||
|
StackBase = (ULONG_PTR)Thread->Tcb.InitialStack;
|
||
|
|
||
|
ASSERT(StackBase > STACK_POINTER(Context));
|
||
|
|
||
|
|
||
|
// There is a valid stack. Note that we limit the size of
|
||
|
// the triage dump stack to MAX_TRIAGE_STACK_SIZE (currently
|
||
|
// 16 KB).
|
||
|
|
||
|
|
||
|
StartOfStackRegion = (ULONG_PTR)STACK_POINTER(Context);
|
||
|
SizeOfSection = (ULONG)min(StackBase - (ULONG_PTR)STACK_POINTER(Context),
|
||
|
MAX_TRIAGE_STACK_SIZE
|
||
|
);
|
||
|
|
||
|
ASSERT(StartOfStackRegion + SizeOfSection <= StackBase);
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
|
||
|
// There is not a valid stack.
|
||
|
|
||
|
|
||
|
SizeOfSection = 0;
|
||
|
}
|
||
|
|
||
|
if (SizeOfSection && Offset + SizeOfSection < BufferSize) {
|
||
|
TriageDumpHeader->CallStackOffset = (ULONG)Offset;
|
||
|
TriageDumpHeader->SizeOfCallStack = SizeOfSection;
|
||
|
TriageDumpHeader->BaseOfStack = (ULONG)StartOfStackRegion;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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 && (Offset + SizeOfSection < BufferSize)) {
|
||
|
TriageDumpHeader->DriverListOffset = (ULONG)Offset;
|
||
|
TriageDumpHeader->DriverCount = DriverCount;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
SizeOfSection = 0;
|
||
|
SizeOfStringData = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the String Pool offset.
|
||
|
|
||
|
|
||
|
SizeOfSection = ALIGN_8(SizeOfStringData +
|
||
|
DriverCount * (sizeof(WCHAR) + sizeof(DUMP_STRING)));
|
||
|
|
||
|
if (SizeOfSection && (Offset + SizeOfSection < BufferSize)) {
|
||
|
TriageDumpHeader->StringPoolOffset = (ULONG)Offset;
|
||
|
TriageDumpHeader->StringPoolSize = SizeOfSection;
|
||
|
Offset += SizeOfSection;
|
||
|
}
|
||
|
|
||
|
|
||
|
BytesToWrite = (ULONG)Offset;
|
||
|
BufferAddress = ((ULONG_PTR)Buffer) - PAGE_SIZE;
|
||
|
|
||
|
|
||
|
// Write the Mm information.
|
||
|
|
||
|
|
||
|
if (TriageDumpHeader->MmOffset) {
|
||
|
|
||
|
Address = (LPVOID)(BufferAddress + TriageDumpHeader->MmOffset);
|
||
|
MmWriteTriageInformation(Address);
|
||
|
}
|
||
|
|
||
|
if (TriageDumpHeader->UnloadedDriversOffset) {
|
||
|
|
||
|
Address = (LPVOID)(BufferAddress + TriageDumpHeader->UnloadedDriversOffset);
|
||
|
MmWriteUnloadedDriverInformation(Address);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Write the PRCB.
|
||
|
|
||
|
|
||
|
if (TriageDumpHeader->PrcbOffset) {
|
||
|
|
||
|
Address = (LPVOID)(BufferAddress + TriageDumpHeader->PrcbOffset);
|
||
|
RtlCopyMemory(Address,
|
||
|
KeGetCurrentPrcb(),
|
||
|
sizeof(KPRCB)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Write the EPROCESS.
|
||
|
|
||
|
|
||
|
if (TriageDumpHeader->ProcessOffset) {
|
||
|
|
||
|
Address = (LPVOID)(BufferAddress + TriageDumpHeader->ProcessOffset);
|
||
|
RtlCopyMemory(Address,
|
||
|
PsGetCurrentProcess(),
|
||
|
sizeof(EPROCESS)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Write the ETHREAD.
|
||
|
|
||
|
|
||
|
if (TriageDumpHeader->ThreadOffset) {
|
||
|
|
||
|
Address = (LPVOID)(BufferAddress + TriageDumpHeader->ThreadOffset);
|
||
|
RtlCopyMemory(Address,
|
||
|
KeGetCurrentThread(),
|
||
|
sizeof(ETHREAD)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Write the Call Stack.
|
||
|
|
||
|
|
||
|
if (TriageDumpHeader->CallStackOffset) {
|
||
|
|
||
|
ASSERT(Thread);
|
||
|
ASSERT(TriageDumpHeader->SizeOfCallStack != 0);
|
||
|
|
||
|
Address = (LPVOID)(BufferAddress + TriageDumpHeader->CallStackOffset);
|
||
|
RtlCopyMemory(Address,
|
||
|
(PVOID)StartOfStackRegion,
|
||
|
TriageDumpHeader->SizeOfCallStack
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Write the Driver List.
|
||
|
|
||
|
|
||
|
if (TriageDumpHeader->DriverListOffset &&
|
||
|
TriageDumpHeader->StringPoolOffset) {
|
||
|
|
||
|
Status = IopWriteDriverList(BufferAddress,
|
||
|
BufferSize,
|
||
|
TriageDumpHeader->DriverListOffset,
|
||
|
TriageDumpHeader->StringPoolOffset
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
TriageDumpHeader->DriverListOffset = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT(BytesToWrite < BufferSize);
|
||
|
ASSERT(ALIGN_UP(BytesToWrite, PAGE_SIZE) < BufferSize);
|
||
|
|
||
|
IoDebugPrint((3, "TRIAGEDUMP: Calling IopWriteToDisk with:\n"
|
||
|
"\tBytesToWrite = %#x\n"
|
||
|
"\tBytesToWrite (Aligned) = %#x\n"
|
||
|
"\tBufferSize = %#x\n",
|
||
|
BytesToWrite,
|
||
|
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
|
||
|
);
|
||
|
|
||
|
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;
|
||
|
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);
|
||
|
|
||
|
|
||
|
// Initialization
|
||
|
|
||
|
|
||
|
TempMdl = (PMDL)&MdlHack;
|
||
|
Mcb = *McbBuffer;
|
||
|
BytesToWrite = PAGE_SIZE;
|
||
|
|
||
|
|
||
|
|
||
|
// Initialze the MDL to point to this page.
|
||
|
|
||
|
|
||
|
MmInitializeMdl(TempMdl, NULL, PAGE_SIZE);
|
||
|
|
||
|
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 MappedSsytemVa 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);
|
||
|
|
||
|
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) {
|
||
|
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 - A message buffer we can use to update percentage complete
|
||
|
status.
|
||
|
|
||
|
Mcb - Message Control Block where the data is to be written.
|
||
|
|
||
|
DriverTransferSize - The maximum transfer size for the driver.
|
||
|
|
||
|
Return Values:
|
||
|
|
||
|
NTSTATUS code.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PVOID Va;
|
||
|
PFN_NUMBER PageFrameIndex;
|
||
|
PHYSICAL_ADDRESS PhysicalAddress;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
ULONG WriteCount;
|
||
|
ULONG MaxWriteCount;
|
||
|
ULONG Step;
|
||
|
|
||
|
|
||
|
ASSERT(DriverWriteRoutine != NULL);
|
||
|
ASSERT(Mcb != NULL);
|
||
|
ASSERT(DriverTransferSize != 0);
|
||
|
|
||
|
|
||
|
MaxWriteCount = RtlNumberOfSetBits(PageMap);
|
||
|
Step = MaxWriteCount / 100;
|
||
|
|
||
|
IoDebugPrint((1, "IODUMP: 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) {
|
||
|
|
||
|
sprintf(MessageBuffer,
|
||
|
"%Z: %3d\r",
|
||
|
ProgressMessage,
|
||
|
(WriteCount * 100) / MaxWriteCount);
|
||
|
|
||
|
InbvDisplayString(MessageBuffer);
|
||
|
}
|
||
|
|
||
|
ASSERT(WriteCount <= MaxWriteCount);
|
||
|
|
||
|
|
||
|
// Write the page to disk.
|
||
|
|
||
|
|
||
|
Status = IopWritePageToDisk(
|
||
|
DriverWriteRoutine,
|
||
|
&Mcb,
|
||
|
DriverTransferSize,
|
||
|
PageFrameIndex
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
PSUMMARY_DUMP_HEADER
|
||
|
IopInitializeSummaryDump(
|
||
|
IN PDUMP_CONTROL_BLOCK pDcb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine creates a summary dump header. In particular it initializes
|
||
|
a bitmap that contains a map of kernel memory.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
PDUMP_CONTROL_BLOCK - A pointer to the dump control block.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Non-NULL - A pointer to the summary dump header
|
||
|
|
||
|
NULL - Error
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PULONG pdwBlock;
|
||
|
PSUMMARY_DUMP_HEADER pSummaryHeader;
|
||
|
ULONG dwActualPages;
|
||
|
|
||
|
|
||
|
// Get the dump header page.
|
||
|
|
||
|
|
||
|
pdwBlock = pDcb->HeaderPage;
|
||
|
|
||
|
|
||
|
// The summary dump starts 1 page after the header.
|
||
|
|
||
|
|
||
|
pSummaryHeader = (PSUMMARY_DUMP_HEADER)&pdwBlock[DH_SUMMARY_DUMP_RECORD];
|
||
|
|
||
|
|
||
|
// Fill the header with signatures.
|
||
|
|
||
|
RtlFillMemoryUlong(pSummaryHeader,
|
||
|
sizeof(SUMMARY_DUMP_HEADER),
|
||
|
'PMDS');
|
||
|
|
||
|
|
||
|
// Set the size and valid signature.
|
||
|
|
||
|
|
||
|
pSummaryHeader->BitmapSize = (ULONG)(MmPhysicalMemoryBlock->Run[MmPhysicalMemoryBlock->NumberOfRuns - 1].BasePage +
|
||
|
MmPhysicalMemoryBlock->Run[MmPhysicalMemoryBlock->NumberOfRuns - 1].PageCount);
|
||
|
pSummaryHeader->ValidDump = 'PMUD';
|
||
|
|
||
|
|
||
|
// Construct the kernel memory bitmap.
|
||
|
|
||
|
|
||
|
dwActualPages = IopCreateSummaryDump(pSummaryHeader);
|
||
|
|
||
|
IoDebugPrint((2, "[IopInitializeSummaryDump]: Kernel Pages = %x\n", dwActualPages));
|
||
|
|
||
|
if (!dwActualPages) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the actual number of physical pages in the summary dump
|
||
|
|
||
|
|
||
|
pSummaryHeader->Pages = dwActualPages;
|
||
|
pSummaryHeader->HeaderSize = pDcb->HeaderSize;
|
||
|
|
||
|
return pSummaryHeader;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IopWriteSummaryHeader(
|
||
|
IN PSUMMARY_DUMP_HEADER pSummaryHeader,
|
||
|
IN PDUMP_DRIVER_WRITE pfWrite,
|
||
|
IN OUT PLARGE_INTEGER * McbBuffer,
|
||
|
IN OUT PMDL pMdl,
|
||
|
IN ULONG dwWriteSize,
|
||
|
IN ULONG dwLength
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Write the summary dump header to the dump file.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pSummaryHeader - pointer to the summary dump bitmap
|
||
|
|
||
|
pfWrite - dump driver write function
|
||
|
|
||
|
McbBuffer - pointer to the MCB array.
|
||
|
|
||
|
pMdl - Pointer to an MDL
|
||
|
|
||
|
dwWriteSize - the max transfer size for the dump driver
|
||
|
|
||
|
dwLength - the length of this transfer
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Updated MCB
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
ULONG dwBytesRemaining;
|
||
|
ULONG_PTR dwMemoryAddress;
|
||
|
ULONG dwByteOffset;
|
||
|
ULONG dwByteCount;
|
||
|
PLARGE_INTEGER pMcb;
|
||
|
|
||
|
dwBytesRemaining = dwLength;
|
||
|
dwMemoryAddress = (ULONG_PTR)pSummaryHeader;
|
||
|
pMcb = *McbBuffer;
|
||
|
|
||
|
IoDebugPrint((0, "IoWriteCrashDump: Writing SUMMARY dump header to disk\n"));
|
||
|
|
||
|
while (dwBytesRemaining) {
|
||
|
|
||
|
// Calculate byte offset
|
||
|
dwByteOffset = (ULONG)(dwMemoryAddress & (PAGE_SIZE - 1));
|
||
|
|
||
|
|
||
|
// See if the number of bytes to write is greator than the crash
|
||
|
// drives max transfer.
|
||
|
|
||
|
|
||
|
if (dwBytesRemaining <= dwWriteSize) {
|
||
|
dwByteCount = dwBytesRemaining;
|
||
|
}
|
||
|
else {
|
||
|
dwByteCount = dwWriteSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If the byteCount is greater than the remaining mcb then correct it.
|
||
|
|
||
|
|
||
|
if (dwByteCount > pMcb[0].QuadPart) {
|
||
|
dwByteCount = pMcb[0].LowPart;
|
||
|
}
|
||
|
|
||
|
pMdl->ByteCount = dwByteCount;
|
||
|
pMdl->ByteOffset = dwByteOffset;
|
||
|
pMdl->MappedSystemVa = (PVOID)dwMemoryAddress;
|
||
|
|
||
|
|
||
|
// Get the actual physical frame and create an mdl.
|
||
|
|
||
|
|
||
|
IopMapVirtualToPhysicalMdl(pMdl, dwMemoryAddress, dwByteCount);
|
||
|
|
||
|
|
||
|
// Write to disk.
|
||
|
|
||
|
Status = pfWrite(&pMcb[1], pMdl);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Adjust bytes remaining.
|
||
|
|
||
|
|
||
|
dwBytesRemaining -= dwByteCount;
|
||
|
dwMemoryAddress += dwByteCount;
|
||
|
|
||
|
pMcb[0].QuadPart = pMcb[0].QuadPart - dwByteCount;
|
||
|
pMcb[1].QuadPart = pMcb[1].QuadPart + dwByteCount;
|
||
|
|
||
|
if (pMcb[0].QuadPart == 0) {
|
||
|
pMcb += 2;
|
||
|
}
|
||
|
|
||
|
if (pMcb[0].QuadPart == 0) {
|
||
|
return STATUS_END_OF_FILE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "IoWriteCrashDump: Writing SUMMARY dump header to disk\n"));
|
||
|
|
||
|
*McbBuffer = pMcb;
|
||
|
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
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
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.
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG dwBytesRemaining;
|
||
|
ULONG_PTR dwMemoryAddress;
|
||
|
ULONG dwByteOffset;
|
||
|
ULONG dwByteCount;
|
||
|
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);
|
||
|
|
||
|
|
||
|
Mcb = *McbBuffer;
|
||
|
dwBytesRemaining = WriteLength;
|
||
|
dwMemoryAddress = (ULONG_PTR)Buffer;
|
||
|
|
||
|
IoDebugPrint((2, "IoWriteToDisk: Writing %d bytes to disk.\n", WriteLength));
|
||
|
|
||
|
while (dwBytesRemaining) {
|
||
|
|
||
|
ASSERT(Mcb[0].QuadPart != 0);
|
||
|
ASSERT(IopDumpControlBlock->FileDescriptorArray <= Mcb &&
|
||
|
(LPBYTE)Mcb < (LPBYTE)IopDumpControlBlock->FileDescriptorArray +
|
||
|
IopDumpControlBlock->FileDescriptorSize
|
||
|
);
|
||
|
|
||
|
dwByteOffset = BYTE_OFFSET(dwMemoryAddress);
|
||
|
|
||
|
|
||
|
// See if the number of bytes to write is greator than the crash
|
||
|
// drives max transfer.
|
||
|
|
||
|
|
||
|
dwByteCount = min(dwBytesRemaining, DriverTransferSize);
|
||
|
|
||
|
|
||
|
// If the byteCount is greater than the remaining mcb then correct it.
|
||
|
|
||
|
|
||
|
if (dwByteCount > Mcb[0].QuadPart) {
|
||
|
dwByteCount = Mcb[0].LowPart;
|
||
|
}
|
||
|
|
||
|
Mdl->ByteCount = dwByteCount;
|
||
|
Mdl->ByteOffset = dwByteOffset;
|
||
|
Mdl->MappedSystemVa = (PVOID)dwMemoryAddress;
|
||
|
|
||
|
|
||
|
// Get the actual physical frame and create an mdl.
|
||
|
|
||
|
|
||
|
IopMapVirtualToPhysicalMdl(Mdl, dwMemoryAddress, dwByteCount);
|
||
|
|
||
|
if (!NT_SUCCESS(DriverWriteRoutine(&Mcb[1], Mdl))) {
|
||
|
IoDebugPrint((1, "IopWriteToDisk: Failed write.\n"));
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Adjust bytes remaining.
|
||
|
|
||
|
|
||
|
ASSERT(dwBytesRemaining >= dwByteCount);
|
||
|
ASSERT(dwByteCount != 0);
|
||
|
|
||
|
dwBytesRemaining -= dwByteCount;
|
||
|
dwMemoryAddress += dwByteCount;
|
||
|
|
||
|
Mcb[0].QuadPart -= dwByteCount;
|
||
|
Mcb[1].QuadPart += dwByteCount;
|
||
|
|
||
|
if (Mcb[0].QuadPart == 0) {
|
||
|
Mcb += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "IopWriteToDisk: Successfully wrote %d bytes.\n", WriteLength));
|
||
|
|
||
|
*McbBuffer = Mcb;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
IopMapVirtualToPhysicalMdl(
|
||
|
IN OUT PMDL pMdl,
|
||
|
IN ULONG_PTR dwMemoryAddress,
|
||
|
IN ULONG dwLength
|
||
|
)
|
||
|
{
|
||
|
PPFN_NUMBER pdwPage;
|
||
|
ULONG dwPages;
|
||
|
ULONG dwBase;
|
||
|
ULONG dwCurrentBase;
|
||
|
ULONG_PTR dwBaseVa;
|
||
|
PHYSICAL_ADDRESS PhysicalAddress;
|
||
|
|
||
|
|
||
|
// Begin by determining the base physical page of the start of the address
|
||
|
// range and filling in the MDL appropriately.
|
||
|
|
||
|
|
||
|
pMdl->StartVa = PAGE_ALIGN((PVOID)dwMemoryAddress);
|
||
|
pMdl->ByteOffset = (ULONG)(dwMemoryAddress & (PAGE_SIZE - 1));
|
||
|
pMdl->ByteCount = dwLength;
|
||
|
dwBaseVa = dwMemoryAddress & ~(PAGE_SIZE - 1);
|
||
|
pMdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
|
||
|
|
||
|
|
||
|
// Compute the number of pages spanned
|
||
|
|
||
|
|
||
|
dwPages = COMPUTE_PAGES_SPANNED(dwMemoryAddress, dwLength);
|
||
|
pdwPage = MmGetMdlPfnArray(pMdl);
|
||
|
|
||
|
|
||
|
// Map all of the pages for this transfer until there are no more remaining
|
||
|
// to be mapped.
|
||
|
|
||
|
IoDebugPrint((3, "MapVirtualToPhysical: VA: %08x off: %08x Len:%08x base: %08x \n", pMdl->StartVa, pMdl->ByteOffset, pMdl->ByteCount, dwBaseVa));
|
||
|
|
||
|
while (dwPages) {
|
||
|
PhysicalAddress = MmGetPhysicalAddress((PVOID)dwBaseVa);
|
||
|
IoDebugPrint((3, "Physical Frame: %08x Adr: %08x\n", (PhysicalAddress.QuadPart >> PAGE_SHIFT), dwBaseVa));
|
||
|
*pdwPage++ = (PFN_NUMBER)(PhysicalAddress.QuadPart >> PAGE_SHIFT);
|
||
|
dwBaseVa += PAGE_SIZE;
|
||
|
dwPages--;
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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 PSUMMARY_DUMP_HEADER pHeader
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
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:
|
||
|
|
||
|
pHeader - Pointer to the dump header
|
||
|
|
||
|
pDcb - Dump control block
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PRTL_BITMAP pBitMapHeader = (PRTL_BITMAP)(pHeader + 1);
|
||
|
ULONG Pages;
|
||
|
PCHAR VA;
|
||
|
ULONG UserPages;
|
||
|
PHYSICAL_ADDRESS PhyAddr;
|
||
|
ULONG i;
|
||
|
PULONG pBitMapBuffer;
|
||
|
PUCHAR pBitMapBytes;
|
||
|
LARGE_INTEGER dumpFileSize;
|
||
|
PULONG block;
|
||
|
ULONG numDumpPages;
|
||
|
|
||
|
|
||
|
// The bitmap is the last structure in the header. The
|
||
|
// buffer is allocated directly after the bitmap header.
|
||
|
|
||
|
// Initialize Bit Map, set the size and buffer address.
|
||
|
|
||
|
|
||
|
pBitMapHeader->SizeOfBitMap = pHeader->BitmapSize;
|
||
|
pBitMapBuffer = (PULONG)(pBitMapHeader + 1);
|
||
|
pBitMapHeader->Buffer = pBitMapBuffer;
|
||
|
pBitMapBytes = (PUCHAR)pBitMapBuffer;
|
||
|
|
||
|
|
||
|
// Clear all bits
|
||
|
|
||
|
|
||
|
RtlClearAllBits(pBitMapHeader);
|
||
|
|
||
|
|
||
|
// Have MM initialize the kernel memory to dump
|
||
|
|
||
|
|
||
|
MmSetKernelDumpRange(pHeader);
|
||
|
|
||
|
|
||
|
// Exclude non-existent memory
|
||
|
|
||
|
|
||
|
IopDeleteNonExistentMemory(pHeader, MmPhysicalMemoryBlock);
|
||
|
|
||
|
|
||
|
Pages = RtlNumberOfSetBits(pBitMapHeader);
|
||
|
IoDebugPrint((2, "IopLocalSetBits = %x\n", Pages));
|
||
|
|
||
|
|
||
|
// See If we have room to Include user va for the current process
|
||
|
|
||
|
|
||
|
block = IopDumpControlBlock->HeaderPage;
|
||
|
dumpFileSize.LowPart = block[DH_REQUIRED_DUMP_SPACE];
|
||
|
dumpFileSize.HighPart = block[DH_REQUIRED_DUMP_SPACE + 1];
|
||
|
|
||
|
dumpFileSize.QuadPart -= IopDumpControlBlock->HeaderSize;
|
||
|
|
||
|
|
||
|
// Total physical pages will not exceed 2<<32 for sometime
|
||
|
|
||
|
|
||
|
numDumpPages = (ULONG)(dumpFileSize.QuadPart >> PAGE_SHIFT);
|
||
|
IoDebugPrint((2, "IopCreateSummaryDump: numDumpPages: %x Pages : %x\n", numDumpPages, Pages));
|
||
|
|
||
|
|
||
|
// Only copy user virtual if there is enough room in the dump file.
|
||
|
|
||
|
|
||
|
UserPages = 0;
|
||
|
|
||
|
if (Pages < numDumpPages) {
|
||
|
|
||
|
|
||
|
// Stride through user VA and include all valid pages
|
||
|
|
||
|
|
||
|
for (VA = 0; VA < (PCHAR)MmHighestUserAddress; VA += PAGE_SIZE) {
|
||
|
|
||
|
if (MmIsAddressValid(VA)) {
|
||
|
|
||
|
if (NT_SUCCESS(IoSetDumpRange(pHeader, VA, 1, FALSE))) {
|
||
|
|
||
|
UserPages++;
|
||
|
|
||
|
|
||
|
// Break if there is no more room in the dump file
|
||
|
|
||
|
|
||
|
if (UserPages + Pages >= numDumpPages) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
Pages += UserPages;
|
||
|
|
||
|
IoDebugPrint((2, "IopCreateSummaryDump number of user mode pages = %x\n", UserPages));
|
||
|
|
||
|
IoDebugPrint((2, "SummaryDump: Dump SummaryDumpHeader\n"));
|
||
|
IoDebugPrint((2, "SdBitmapSize =%x\n", pHeader->BitmapSize));
|
||
|
IoDebugPrint((2, "SdAllBits =%x\n", Pages));
|
||
|
IoDebugPrint((2, "&SdBitmapBuffer[0]=%x\n", pBitMapHeader->Buffer));
|
||
|
|
||
|
|
||
|
return Pages;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
IopDeleteNonExistentMemory(
|
||
|
PSUMMARY_DUMP_HEADER pHeader,
|
||
|
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)
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PPHYSICAL_MEMORY_RUN Run;
|
||
|
ULONG NumberOfRuns;
|
||
|
ULONG i;
|
||
|
PFN_NUMBER CurrentPageFrame, NextPageFrame;
|
||
|
PRTL_BITMAP pBitMapHeader;
|
||
|
|
||
|
NumberOfRuns = MmPhysicalMemoryBlock->NumberOfRuns;
|
||
|
|
||
|
Run = &MmPhysicalMemoryBlock->Run[0];
|
||
|
|
||
|
pBitMapHeader = (PRTL_BITMAP)(pHeader + 1);
|
||
|
i = 0;
|
||
|
|
||
|
CurrentPageFrame = 1;
|
||
|
|
||
|
NextPageFrame = Run->BasePage;
|
||
|
|
||
|
|
||
|
// Remove the gap from 0 till the first run.
|
||
|
|
||
|
|
||
|
if (NextPageFrame > CurrentPageFrame) {
|
||
|
|
||
|
IoDebugPrint((2, "DeleteNonExistentMemory: %x - %x\n",
|
||
|
CurrentPageFrame,
|
||
|
(NextPageFrame - CurrentPageFrame)));
|
||
|
|
||
|
|
||
|
// FIXFIX - handle page frame numbers greater than 32 bits.
|
||
|
|
||
|
|
||
|
IopRemovePageFromPageMap(pHeader->BitmapSize,
|
||
|
pBitMapHeader,
|
||
|
(ULONG)CurrentPageFrame,
|
||
|
(ULONG)(NextPageFrame - CurrentPageFrame)
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove the gaps between runs.
|
||
|
|
||
|
|
||
|
while (i < NumberOfRuns - 1) {
|
||
|
|
||
|
CurrentPageFrame = Run->BasePage + Run->PageCount;
|
||
|
IoDebugPrint((2, "Run[%x]: Base=%x, Pages=%x\n", i, Run->BasePage, Run->PageCount));
|
||
|
|
||
|
i++;
|
||
|
Run++;
|
||
|
|
||
|
|
||
|
// Get the next starting BasePage.
|
||
|
|
||
|
|
||
|
NextPageFrame = Run->BasePage;
|
||
|
|
||
|
if (NextPageFrame != CurrentPageFrame) {
|
||
|
|
||
|
IoDebugPrint((2, "DeleteNonExistentMemory: %x - %x\n", NextPageFrame, CurrentPageFrame));
|
||
|
|
||
|
|
||
|
// FIXFIX - handle page frame numbers greater than 32 bits.
|
||
|
|
||
|
|
||
|
IopRemovePageFromPageMap(pHeader->BitmapSize,
|
||
|
pBitMapHeader,
|
||
|
(ULONG)CurrentPageFrame,
|
||
|
(ULONG)(NextPageFrame - CurrentPageFrame)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
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;
|
||
|
|
||
|
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)) {
|
||
|
|
||
|
KdPrint(("IODUMP: Cannot dump to pagefile on non-system partition.\n"));
|
||
|
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)) {
|
||
|
KdPrint(("CRASHDUMP: Failed ZwQueryInformationFile\n"));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Do not ask for more space than is in the pagefile.
|
||
|
|
||
|
|
||
|
RequestedFileSize = IopDumpControlBlock->DumpFileSize;
|
||
|
|
||
|
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)) {
|
||
|
KdPrint(("CRASHDUMP: ZwFsControlFile returnd %d\n", 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;
|
||
|
NTSTATUS dwStatus;
|
||
|
BOOLEAN ShouldFreeDCB;
|
||
|
|
||
|
dcb = IopDumpControlBlock;
|
||
|
|
||
|
|
||
|
if (dcb) {
|
||
|
|
||
|
if (dcb->MemoryDescriptor) {
|
||
|
ExFreePool(dcb->MemoryDescriptor);
|
||
|
dcb->MemoryDescriptor = NULL;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
ShouldFreeDCB = FreeDCB | !(dcb->Flags & DCB_AUTO_REBOOT);
|
||
|
|
||
|
|
||
|
// Disable all options that require dump file access
|
||
|
|
||
|
|
||
|
dcb->Flags = dcb->Flags & DCB_AUTO_REBOOT;
|
||
|
|
||
|
IoDebugPrint((2, "[IopFreeDCB] Should Free DCB = %s\n", (ShouldFreeDCB ? "TRUE" : "FALSE")));
|
||
|
|
||
|
if (ShouldFreeDCB) {
|
||
|
IopDumpControlBlock = NULL;
|
||
|
ExFreePool(dcb);
|
||
|
IoDebugPrint((2, "[IopFreeDCB]: Freeing Dump Control Block\n"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IoDebugPrint((2, "[IopFreeDCB]: CRASH DUMP DISABLED\n"));
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IoSetCrashDumpState(
|
||
|
IN SYSTEM_CRASH_STATE_INFORMATION *pDumpState
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Disable the current crash dump. Optionally, configure a new crash dump.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pDumpState - If ValidDirectDump = TRUE enable a new dump
|
||
|
FALSE Disable crash dump
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_SUCCESS - The operation was successful
|
||
|
|
||
|
W32ERROR - Otherwise
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
ULONG crashConfigurationInProgress;
|
||
|
|
||
|
|
||
|
// Check the sentinal
|
||
|
|
||
|
|
||
|
crashConfigurationInProgress = InterlockedExchange(&IopCrashDumpStateChange, 1);
|
||
|
|
||
|
if (crashConfigurationInProgress) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If crash dumps disabled return success
|
||
|
|
||
|
|
||
|
if (!pDumpState->ValidDirectDump) {
|
||
|
IopFreeDCB(FALSE);
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
else {
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
}
|
||
|
|
||
|
InterlockedExchange(&IopCrashDumpStateChange, 0);
|
||
|
|
||
|
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.
|
||
|
--*/
|
||
|
{
|
||
|
HANDLE keyHandle;
|
||
|
HANDLE crashHandle;
|
||
|
LOGICAL crashHandleOpened;
|
||
|
UNICODE_STRING keyName;
|
||
|
NTSTATUS status;
|
||
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
PDUMP_CONTROL_BLOCK dcb;
|
||
|
ULONG handleValue;
|
||
|
|
||
|
*dumpControl = 0;
|
||
|
*autoReboot = 0;
|
||
|
*dumpFileSize = 0;
|
||
|
*numberOfHeaderPages = 1; // 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 equals summary dump or the amount of physical memory is greater than 2GB, then enable summary dump
|
||
|
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) || (MmPhysicalMemoryBlock->NumberOfPages >= (0x80000000 >> PAGE_SHIFT))) {
|
||
|
*dumpControl |= DCB_SUMMARY_DUMP_ENABLED;
|
||
|
IoDebugPrint((2, "IopInitializeDCB: SUMMARY DUMP ENABLED \n"));
|
||
|
|
||
|
// Allocate enough storage for the dump header, summary dump header and bitmap.
|
||
|
*numberOfHeaderPages = BYTES_TO_PAGES(PAGE_SIZE + ((MmPhysicalMemoryBlock->NumberOfPages >> 5) << 2) + sizeof(SUMMARY_DUMP_HEADER));
|
||
|
|
||
|
IoDebugPrint((2, "IopInitializeDCB: NumberOfHeader Pages = %x\n", numberOfHeaderPages));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
HANDLE keyHandle;
|
||
|
HANDLE crashHandle;
|
||
|
LOGICAL crashHandleOpened;
|
||
|
UNICODE_STRING keyName;
|
||
|
NTSTATUS status;
|
||
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
PDUMP_CONTROL_BLOCK dcb;
|
||
|
ULONG dumpControl;
|
||
|
ULONG handleValue;
|
||
|
ULONG autoReboot;
|
||
|
PCHAR partitionName;
|
||
|
ULONG dcbSize;
|
||
|
LARGE_INTEGER page;
|
||
|
ULONG numberOfHeaderPages;
|
||
|
ULONG dumpFileSize;
|
||
|
|
||
|
|
||
|
// Read all the registry default values first.
|
||
|
|
||
|
|
||
|
IopReadDumpRegistry(&dumpControl,
|
||
|
&numberOfHeaderPages,
|
||
|
&autoReboot,
|
||
|
&dumpFileSize);
|
||
|
|
||
|
|
||
|
// If we aren't crashing or auto rebooting then return now.
|
||
|
|
||
|
|
||
|
if (dumpControl == 0 && autoReboot == 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);
|
||
|
// *autoReboot = 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 = ExAllocatePoolWithTag(NonPagedPool, dcbSize, 'pmuD');
|
||
|
if (!dcb) {
|
||
|
IoDebugPrint((1, "IopInitializeDCB: Not enough pool to allocate DCB0\n"));
|
||
|
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 | (autoReboot ? DCB_AUTO_REBOOT : 0));
|
||
|
dcb->NumberProcessors = KeNumberProcessors;
|
||
|
dcb->ProcessorArchitecture = KeProcessorArchitecture;
|
||
|
dcb->MinorVersion = (USHORT)NtBuildNumber;
|
||
|
dcb->MajorVersion = (USHORT)((NtBuildNumber >> 28) & 0xfffffff);
|
||
|
dcb->BuildNumber = CmNtCSDVersion;
|
||
|
dcb->TriageDumpFlags = DEFAULT_TRIAGE_DUMP_FLAGS;
|
||
|
|
||
|
dcb->DumpFileSize.QuadPart = dumpFileSize;
|
||
|
|
||
|
|
||
|
// Allocate memory descriptors.
|
||
|
|
||
|
|
||
|
dcb->MemoryDescriptorLength = sizeof(PHYSICAL_MEMORY_DESCRIPTOR) - sizeof(PHYSICAL_MEMORY_RUN) +
|
||
|
(MmPhysicalMemoryBlock->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RUN));
|
||
|
dcb->MemoryDescriptor = ExAllocatePoolWithTag(NonPagedPool, dcb->MemoryDescriptorLength, 'pmuD');
|
||
|
if (!dcb->MemoryDescriptor) {
|
||
|
ExFreePool(dcb);
|
||
|
|
||
|
IoDebugPrint((1, "IopInitializeDCB: Not enough pool to allocate DCB1\n"));
|
||
|
IopLogErrorEvent(0, 1, STATUS_SUCCESS, IO_DUMP_INITIALIZATION_FAILURE, 0, NULL, 0, NULL);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
dcb->MemoryDescriptor,
|
||
|
MmPhysicalMemoryBlock,
|
||
|
dcb->MemoryDescriptorLength
|
||
|
);
|
||
|
|
||
|
|
||
|
// Allocate header page.
|
||
|
|
||
|
|
||
|
dcb->HeaderSize = numberOfHeaderPages * PAGE_SIZE;
|
||
|
dcb->HeaderPage = ExAllocatePoolWithTag(NonPagedPool, dcb->HeaderSize, 'pmuD');
|
||
|
|
||
|
if (!dcb->HeaderPage) {
|
||
|
ExFreePool(dcb->MemoryDescriptor);
|
||
|
ExFreePool(dcb);
|
||
|
IoDebugPrint((1, "IopInitializeDCB: Not enough pool to allocate DCB2\n"));
|
||
|
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 = ExAllocatePoolWithTag(NonPagedPool,
|
||
|
dumpFileSize,
|
||
|
'pmuD'
|
||
|
);
|
||
|
|
||
|
if (!dcb->TriageDumpBuffer) {
|
||
|
ExFreePool(dcb->HeaderPage);
|
||
|
ExFreePool(dcb->MemoryDescriptor);
|
||
|
ExFreePool(dcb);
|
||
|
IoDebugPrint((1, "IopInitializeDCB: Not enough pool to allocate DCB3\n"));
|
||
|
IopLogErrorEvent(0, 1, STATUS_SUCCESS, IO_DUMP_INITIALIZATION_FAILURE, 0, NULL, 0, NULL);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
dcb->TriageDumpBufferSize = dumpFileSize;
|
||
|
}
|
||
|
|
||
|
IopDumpControlBlock = dcb;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
IopConfigureCrashDump(
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG dwStatus;
|
||
|
PFILE_OBJECT fileObject;
|
||
|
PDEVICE_OBJECT deviceObject;
|
||
|
|
||
|
|
||
|
// Only Init DCB Once.
|
||
|
|
||
|
|
||
|
if (!IopDumpControlBlock) {
|
||
|
if (!IopInitializeDCB()) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return crash dump not enabled
|
||
|
|
||
|
if (!IopDumpControlBlock) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Only autoreboot?
|
||
|
|
||
|
|
||
|
if (!(IopDumpControlBlock->Flags & (DCB_DUMP_ENABLED | DCB_SUMMARY_ENABLED))) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Configure the paging file for crash dump.
|
||
|
|
||
|
|
||
|
IoDebugPrint((2, "[IoPageFileCreated]: Page file dump\n"));
|
||
|
|
||
|
|
||
|
dwStatus = ObReferenceObjectByHandle(
|
||
|
hPageFile,
|
||
|
0,
|
||
|
IoFileObjectType,
|
||
|
KernelMode,
|
||
|
(PVOID *)&fileObject,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(dwStatus)) {
|
||
|
IoDebugPrint((1, "[IoPageFileCreated]: ObReferenceObjectByHandle for Paging file failed status = %x\n", 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);
|
||
|
|
||
|
|
||
|
// If this device object does not represents the boot partition return
|
||
|
// FALSE so MM will try again.
|
||
|
|
||
|
|
||
|
if (!(deviceObject->Flags & DO_SYSTEM_BOOT_PARTITION)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Load paging file dump stack
|
||
|
|
||
|
|
||
|
dwStatus = IoGetDumpStack(L"dump_",
|
||
|
&IopDumpControlBlock->DumpStack,
|
||
|
DeviceUsageTypeDumpFile,
|
||
|
FALSE);
|
||
|
|
||
|
if (!NT_SUCCESS(dwStatus)) {
|
||
|
IoDebugPrint((1, "IoPageFileCreated: Could not load dump stack status = %x\n", dwStatus));
|
||
|
goto error_return;
|
||
|
}
|
||
|
|
||
|
IopDumpControlBlock->DumpStack->Init.CrashDump = TRUE;
|
||
|
|
||
|
IopDumpControlBlock->DumpStack->Init.MemoryBlock = ExAllocatePoolWithTag(
|
||
|
NonPagedPool,
|
||
|
IO_DUMP_MEMORY_BLOCK_PAGES * PAGE_SIZE,
|
||
|
'pmuD'
|
||
|
);
|
||
|
|
||
|
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,
|
||
|
IopDumpControlBlock->MemoryDescriptor->NumberOfPages,
|
||
|
IopDumpControlBlock->MemoryDescriptor->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)) {
|
||
|
IoDebugPrint((1, "IopPageFileCreated: Page File dump init FAILED status = %x\n", dwStatus));
|
||
|
|
||
|
IopLogErrorEvent(0, 3, STATUS_SUCCESS, IO_DUMP_PAGE_CONFIG_FAILED, 0, NULL, 0, NULL);
|
||
|
|
||
|
IopFreeDCB(FALSE);
|
||
|
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Debugging routines.
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
ULONG IopDebugLevel = 0;
|
||
|
UCHAR IopBuffer[128];
|
||
|
|
||
|
|
||
|
VOID IopDebugPrint(ULONG Level, PCHAR Format, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
|
||
|
va_start(ap, Format);
|
||
|
|
||
|
if (IopDebugLevel >= Level) {
|
||
|
vsprintf(IopBuffer, Format, ap);
|
||
|
DbgPrint(IopBuffer);
|
||
|
}
|
||
|
|
||
|
va_end(ap);
|
||
|
}
|
||
|
|
||
|
#endif //DBG
|