NT4/private/ntos/io/dumpctl.c
2020-09-30 17:12:29 +02:00

705 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dumpctl.c
Abstract:
This module contains the code to dump memory to disk after a crash.
Author:
Darryl E. Havens (darrylh) 17-dec-1993
Environment:
Kernel mode
Revision History:
--*/
#include "iop.h"
//
// Forward declarations
//
static
VOID
MapPhysicalMemory(
IN OUT PMDL Mdl,
IN ULONG MemoryAddress,
IN PPHYSICAL_MEMORY_RUN PhysicalMemoryRun,
IN ULONG Length
);
extern PULONG MmPfnDatabase;
NTSTATUS IopFinalCrashDumpStatus = -1;
#if 0
#if DBG
ULONG BreakDiskByteOffset;
ULONG BreakPfn;
#endif // DBG
#endif // 0
BOOLEAN
IoWriteCrashDump(
IN ULONG BugCheckCode,
IN ULONG BugCheckParameter1,
IN ULONG BugCheckParameter2,
IN ULONG BugCheckParameter3,
IN ULONG 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;
INITIALIZATION_CONTEXT initContext;
PLIST_ENTRY nextEntry;
PMINIPORT_NODE mpNode;
PDUMP_DRIVER_OPEN open;
PDUMP_DRIVER_WRITE write;
PDUMP_DRIVER_FINISH finishUp;
PDUMP_HEADER header;
EXCEPTION_RECORD exception;
PCONTEXT context = ContextSave;
PULONG block;
LARGE_INTEGER diskByteOffset;
PULONG page;
ULONG localMdl[(sizeof( MDL )/4) + 17];
PMDL mdl;
PLARGE_INTEGER mcb;
ULONG memoryAddress;
ULONG byteOffset;
ULONG byteCount;
ULONG bytesRemaining;
NTSTATUS status;
PMAPPED_ADDRESS addresses;
ULONG addressCount;
ULONG addressChecksum;
UCHAR messageBuffer[128];
//
// 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.
//
// The rules for determining this are as follows:
//
// 1) The dump control block must be a dump control block.
//
// 2) The dump control block structure checksum must match.
//
// 3) The disk dump driver checksum must match.
//
// 4) The miniport queue must be consistent and all driver
// checksums must match.
//
// 5) The file descriptor pointer must be valid and its checksum
// must match.
//
// 6) All buffers must have been allocated.
//
// 7) The module list address must be valid.
//
if (dcb->Type != IO_TYPE_DCB ||
dcb->Size < sizeof( DUMP_CONTROL_BLOCK ) ||
IopChecksum( dcb, dcb->Size ) != IopDumpControlBlockChecksum ||
///////// IopChecksum( dcb->DiskDumpDriver->DllBase, dcb->DiskDumpDriver->SizeOfImage ) != dcb->DiskDumpChecksum ||
IsListEmpty( &dcb->MiniportQueue ) ||
!dcb->FileDescriptorArray ||
IopChecksum( dcb->FileDescriptorArray, dcb->FileDescriptorSize ) != dcb->FileDescriptorChecksum ||
!dcb->NonCachedBufferVa1 ||
!dcb->NonCachedBufferVa2 ||
!dcb->Buffer ||
!dcb->HeaderPage ||
dcb->LoadedModuleList != &PsLoadedModuleList) {
#if DBG
if (dcb->Type != IO_TYPE_DCB) {
DbgPrint( "DCB Type field is invalid\n" );
}
if (dcb->Size < sizeof( DUMP_CONTROL_BLOCK )) {
DbgPrint( "DCB Size field is invalid\n" );
}
if (IopChecksum( dcb, dcb->Size ) != IopDumpControlBlockChecksum) {
DbgPrint( "DCB checksum is inconsistent\n" );
}
///////// if (IopChecksum( dcb->DiskDumpDriver, dcb->DiskDumpDriver->SizeOfImage) != dcb->DiskDumpChecksum) {
///////// DbgPrint( "Disk dump driver checksum is inconsistent\n" );
///////// }
if (IsListEmpty( &dcb->MiniportQueue )) {
DbgPrint( "Miniport queue is empty\n" );
}
if (!dcb->FileDescriptorArray) {
DbgPrint( "No boot device paging file was found or was too small\n" );
}
if (IopChecksum( dcb->FileDescriptorArray, dcb->FileDescriptorSize ) != dcb->FileDescriptorChecksum) {
DbgPrint( "Page file descriptor checksum is inconsistent\n" );
}
DbgPrint( "CRASHDUMP: Disk dump routine returning due to DCB integrity error\n" );
DbgPrint( " No dump will be created\n" );
#endif // DBG
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
return FALSE;
}
//
// Finally, check the mapped registers in the driver to guarantee that
// they are consistent as well.
//
addressCount = 0;
addressChecksum = 0;
addresses = * (PMAPPED_ADDRESS *) dcb->MappedRegisterBase;
while (addresses) {
addressCount++;
addressChecksum += IopChecksum( addresses, sizeof( MAPPED_ADDRESS ) );
addresses = addresses->NextMappedAddress;
}
if (addressCount != dcb->MappedAddressCount ||
addressChecksum != dcb->MappedAddressChecksum) {
#if DBG
DbgPrint( "Mapped address count or checksum failed\n" );
#endif // DBG
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
return FALSE;
}
//
// The dump control block appears to be in good order. Begin by
// initializing the disk dump driver.
//
initContext.Length = sizeof( INITIALIZATION_CONTEXT );
initContext.DiskSignature = dcb->DiskSignature;
initContext.MemoryBlock = dcb->Buffer;
initContext.CommonBuffer[0] = dcb->NonCachedBufferVa1;
initContext.CommonBuffer[1] = dcb->NonCachedBufferVa2;
initContext.PhysicalAddress[0] = dcb->NonCachedBufferPa1;
initContext.PhysicalAddress[1] = dcb->NonCachedBufferPa2;
initContext.StallRoutine = &KeStallExecutionProcessor;
initContext.AdapterObject = dcb->AdapterObject;
initContext.MappedRegisterBase = dcb->MappedRegisterBase;
initContext.PortConfiguration = dcb->PortConfiguration;
status = ((PDRIVER_INITIALIZE) (dcb->DiskDumpDriver->EntryPoint))( (PDRIVER_OBJECT) NULL,
(PUNICODE_STRING) &initContext );
if (!NT_SUCCESS( status )) {
#if DBG
DbgPrint( "CRASHDUMP: Unable to initialize disk dump driver; error = %x\n", status );
#endif // DBG
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
return FALSE;
}
//
// Record the dump driver's two entry points.
//
open = initContext.OpenRoutine;
write = initContext.WriteRoutine;
finishUp = initContext.FinishRoutine;
//
// Display message on screen that we are starting the crashdump
//
sprintf( messageBuffer, "%Z\n", &dcb->PssInitMsg );
HalDisplayString( messageBuffer );
//
// Now initialize each of the miniport drivers.
//
nextEntry = dcb->MiniportQueue.Flink;
while (nextEntry != &dcb->MiniportQueue) {
mpNode = CONTAINING_RECORD( nextEntry,
MINIPORT_NODE,
ListEntry );
if (IopChecksum( mpNode->DriverEntry->DllBase, mpNode->DriverEntry->SizeOfImage ) != mpNode->DriverChecksum) {
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
return FALSE;
}
status = ((PDRIVER_INITIALIZE) (mpNode->DriverEntry->EntryPoint))( NULL, NULL );
if (!NT_SUCCESS( status )) {
#if DBG
DbgPrint( "CRASHDUMP: Could not initialize miniport; error = %x\n", status );
#endif // DBG
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
return FALSE;
}
nextEntry = nextEntry->Flink;
}
//
// Now attempt to 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 (!open( dcb->PartitionOffset )) {
#if DBG
DbgPrint( "CRASHDUMP: Could not find/open partition offset\n" );
#endif // DBG
IopFinalCrashDumpStatus = STATUS_UNSUCCESSFUL;
return FALSE;
}
//
// 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;
header->DirectoryTableBase = PsInitialSystemProcess->Pcb.DirectoryTableBase[0];
header->PfnDataBase = MmPfnDatabase;
header->PsLoadedModuleList = &PsLoadedModuleList;
header->PsActiveProcessHead = &PsActiveProcessHead;
header->NumberProcessors = dcb->NumberProcessors;
header->MajorVersion = dcb->MajorVersion;
header->MinorVersion = dcb->MinorVersion;
#ifdef i386
header->MachineImageType = IMAGE_FILE_MACHINE_I386;
#endif // i386
#ifdef MIPS
header->MachineImageType = IMAGE_FILE_MACHINE_R4000;
#endif // MIPS
#ifdef ALPHA
header->MachineImageType = IMAGE_FILE_MACHINE_ALPHA;
#endif // ALPHA
#ifdef _PPC_
header->MachineImageType = IMAGE_FILE_MACHINE_POWERPC;
#endif // PPC
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;
#if defined(i386)
exception.ExceptionAddress = (PVOID) context->Eip;
#elif defined(MIPS)
exception.ExceptionAddress = (PVOID) context->Fir;
#elif defined(ALPHA)
exception.ExceptionAddress = (PVOID) context->Fir;
#elif defined(_PPC_)
exception.ExceptionAddress = (PVOID) context->Iar;
#else
#error( "unknown processor type" )
#endif
RtlCopyMemory( &block[DH_EXCEPTION_RECORD],
&exception,
sizeof( EXCEPTION_RECORD ) );
//
// 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 = (PULONG) (mdl + 1);
*page = dcb->HeaderPfn;
mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
bytesRemaining = PAGE_SIZE;
memoryAddress = (ULONG) 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.
//
#if DBG
DbgPrint( "IoWriteCrashDump: Writing dump header to disk\n" );
#endif
while (bytesRemaining) {
if (mcb[0].QuadPart <= bytesRemaining) {
byteCount = mcb[0].LowPart;
} else {
byteCount = bytesRemaining;
}
mdl->ByteCount = byteCount;
mdl->ByteOffset = memoryAddress & (PAGE_SIZE - 1);
mdl->MappedSystemVa = (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;
}
}
#if DBG
DbgPrint( "IoWriteCrashDump: Header page written\n" );
#endif
//
// 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) {
ULONG pagesDoneSoFar = 0;
ULONG currentPercentage = 0;
ULONG maximumPercentage = 0;
#if DBG
DbgPrint( "IoWriteCrashDump: Writing memory dump\n" );
#endif
//
// Set the virtual file offset and initialize loop variables and
// constants.
//
//mdl->MdlFlags &= ~MDL_MAPPED_TO_SYSTEM_VA;
memoryAddress = dcb->MemoryDescriptor->Run[0].BasePage * PAGE_SIZE;
//
// Now loop, writing all of physical memory to the paging file.
//
while (mcb[0].QuadPart) {
diskByteOffset = mcb[1];
//
// Calculate byte offset;
//
byteOffset = memoryAddress & (PAGE_SIZE - 1);
if (32768 <= mcb[0].QuadPart) {
byteCount = 32768 - byteOffset;
} else {
byteCount = mcb[0].LowPart;
}
pagesDoneSoFar += byteCount / PAGE_SIZE;
currentPercentage =
(pagesDoneSoFar * 100) /
dcb->MemoryDescriptor->NumberOfPages;
if (currentPercentage > maximumPercentage) {
maximumPercentage = currentPercentage;
//
// Update message on screen.
//
sprintf( messageBuffer, "%Z: %3d\r", &dcb->PssProgressMsg, maximumPercentage );
HalDisplayString( messageBuffer );
}
//
// Map the physical memory and write it to the
// current segment of the file.
//
MapPhysicalMemory( 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 >= dcb->MemoryDescriptor->NumberOfPages) {
break;
}
}
#if DBG
DbgPrint( "IoWriteCrashDump: Memory dump written\n" );
#endif
}
sprintf( messageBuffer, "%Z", &dcb->PssDoneMsg );
HalDisplayString( 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) {
#if DBG
DbgPrint( "IoWriteCrashDump: Autorebooting\n" );
#endif
KeReturnToFirmware( HalRebootRoutine );
}
return TRUE;
}
static
VOID
MapPhysicalMemory(
IN OUT PMDL Mdl,
IN ULONG 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;
PULONG page;
ULONG pages;
ULONG base;
ULONG currentBase;
//
// Begin by determining the base physical page of the start of the address
// range and filling in the MDL appropriately.
//
Mdl->StartVa = (PVOID) (MemoryAddress);
Mdl->ByteOffset = MemoryAddress & (PAGE_SIZE - 1);
Mdl->ByteCount = Length;
//
// Get the page frame index for the base address.
//
base = (ULONG) Mdl->StartVa >> PAGE_SHIFT;
pages = COMPUTE_PAGES_SPANNED( (ULONG) MemoryAddress, Length );
currentBase = pmr->BasePage;
page = (PULONG) (Mdl + 1);
//
// 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 + (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 );
}