123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742 |
- /*++
- Copyright (c) 2014 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- crashdmp.c
- Abstract:
- This module implements support for collecting and writing out crash dump
- data in the unfortunate event of a fatal system error.
- Author:
- Chris Stevens 26-Aug-2014
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include <minoca/kernel/crashdmp.h>
- #include <minoca/intrface/disk.h>
- #include "kep.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure defines the saved context for a crash dump file.
- Members:
- ListEntry - Stores pointers to the next and previous crash dump files.
- FileHandle - Stores a handle to the open file.
- FileSize - Stores the size of the file, in bytes.
- BlockIoContext - Stores the I/O context necessary to perform block-level
- writes to the crash dump file.
- Device - Stores a pointer to the device the crash dump file ultimately
- writes to.
- DiskInterface - Stores a pointer to the disk interface.
- --*/
- typedef struct _CRASH_DUMP_FILE {
- LIST_ENTRY ListEntry;
- PIO_HANDLE FileHandle;
- ULONGLONG FileSize;
- FILE_BLOCK_IO_CONTEXT BlockIoContext;
- PDEVICE Device;
- PDISK_INTERFACE DiskInterface;
- } CRASH_DUMP_FILE, *PCRASH_DUMP_FILE;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- KepDestroyCrashDumpFile (
- PCRASH_DUMP_FILE CrashDumpFile
- );
- USHORT
- KepCalculateChecksum (
- PVOID Data,
- ULONG DataSize
- );
- VOID
- KepCrashDumpDiskInterfaceNotificationCallback (
- PVOID Context,
- PDEVICE Device,
- PVOID InterfaceBuffer,
- ULONG InterfaceBufferSize,
- BOOL Arrival
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a list of available crash dump files and a lock to protect them.
- //
- KSPIN_LOCK KeCrashDumpListLock;
- LIST_ENTRY KeCrashDumpListHead;
- //
- // Store the UUID of the disk interface.
- //
- UUID KeDiskInterfaceUuid = UUID_DISK_INTERFACE;
- //
- // Store a scratch I/O buffer used to write portions of the crash dump file.
- //
- PIO_BUFFER KeCrashDumpScratchBuffer;
- //
- // Store a boolean indicating whether to write all crash dump files or just
- // stop at the first successfully written one.
- //
- BOOL KeWriteAllCrashDumpFiles;
- //
- // ------------------------------------------------------------------ Functions
- //
- VOID
- KeRegisterCrashDumpFile (
- HANDLE Handle,
- BOOL Register
- )
- /*++
- Routine Description:
- This routine registers a file for use as a crash dump file.
- Arguments:
- Handle - Supplies a handle to the page file to register.
- Register - Supplies a boolean indicating if the page file is registering
- (TRUE) or de-registering (FALSE).
- Return Value:
- None.
- --*/
- {
- PFILE_BLOCK_INFORMATION BlockInformation;
- PFILE_BLOCK_IO_CONTEXT BlockIoContext;
- PCRASH_DUMP_FILE CrashDumpFile;
- PLIST_ENTRY CurrentEntry;
- PDEVICE DiskDevice;
- PDISK_INTERFACE DiskInterface;
- PVOID DiskToken;
- ULONGLONG FileSize;
- PIO_HANDLE IoHandle;
- PCRASH_DUMP_FILE NewCrashDumpFile;
- PCRASH_DUMP_FILE OldCrashDumpFile;
- KSTATUS Status;
- PDEVICE Volume;
- BlockInformation = NULL;
- IoHandle = (PIO_HANDLE)Handle;
- NewCrashDumpFile = NULL;
- OldCrashDumpFile = NULL;
- //
- // If registering the page file, then look up necessary information and
- // create the crash dump file.
- //
- if (Register != FALSE) {
- Status = IoGetDevice(IoHandle, &Volume);
- if (!KSUCCESS(Status)) {
- goto RegisterCrashDumpFileEnd;
- }
- DiskDevice = IoGetDiskDevice(Volume);
- ASSERT(DiskDevice != NULL);
- //
- // Query the file system to get a list of device offsets and sizes for
- // the location of the page file.
- //
- Status = IoGetFileBlockInformation(IoHandle, &BlockInformation);
- if (!KSUCCESS(Status)) {
- goto RegisterCrashDumpFileEnd;
- }
- //
- // Create the crash dump file structure.
- //
- NewCrashDumpFile = MmAllocateNonPagedPool(sizeof(CRASH_DUMP_FILE),
- KE_ALLOCATION_TAG);
- if (NewCrashDumpFile == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto RegisterCrashDumpFileEnd;
- }
- RtlZeroMemory(NewCrashDumpFile, sizeof(CRASH_DUMP_FILE));
- NewCrashDumpFile->FileHandle = IoHandle;
- Status = IoGetFileSize(IoHandle, &FileSize);
- if (!KSUCCESS(Status)) {
- goto RegisterCrashDumpFileEnd;
- }
- NewCrashDumpFile->FileSize = FileSize;
- NewCrashDumpFile->Device = DiskDevice;
- BlockIoContext = &(NewCrashDumpFile->BlockIoContext);
- BlockIoContext->FileBlockInformation = BlockInformation;
- BlockInformation = NULL;
- Status = IoRegisterForInterfaceNotifications(
- &KeDiskInterfaceUuid,
- KepCrashDumpDiskInterfaceNotificationCallback,
- NewCrashDumpFile->Device,
- NewCrashDumpFile,
- TRUE);
- if (!KSUCCESS(Status)) {
- goto RegisterCrashDumpFileEnd;
- }
- //
- // If the interface wasn't immediately filled in, then fail.
- //
- DiskInterface = NewCrashDumpFile->DiskInterface;
- if (DiskInterface == NULL) {
- Status = STATUS_NOT_SUPPORTED;
- goto RegisterCrashDumpFileEnd;
- }
- //
- // The crash dump file is ready to go. Give the disk a heads up that
- // it's block I/O routines may be called into action. This gives it a
- // chance to allocate any memory it may need later.
- //
- if (DiskInterface->BlockIoInitialize != NULL) {
- DiskToken = DiskInterface->DiskToken;
- Status = DiskInterface->BlockIoInitialize(DiskToken);
- if (!KSUCCESS(Status)) {
- goto RegisterCrashDumpFileEnd;
- }
- }
- }
- //
- // Search for a crash file with the same handle.
- //
- KeAcquireSpinLock(&KeCrashDumpListLock);
- CurrentEntry = KeCrashDumpListHead.Next;
- while (CurrentEntry != &KeCrashDumpListHead) {
- CrashDumpFile = LIST_VALUE(CurrentEntry, CRASH_DUMP_FILE, ListEntry);
- if (CrashDumpFile->FileHandle == IoHandle) {
- OldCrashDumpFile = CrashDumpFile;
- LIST_REMOVE(CurrentEntry);
- break;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- if (NewCrashDumpFile != NULL) {
- INSERT_BEFORE(&(NewCrashDumpFile->ListEntry), &KeCrashDumpListHead);
- }
- KeReleaseSpinLock(&KeCrashDumpListLock);
- if (OldCrashDumpFile != NULL) {
- KepDestroyCrashDumpFile(CrashDumpFile);
- }
- Status = STATUS_SUCCESS;
- RegisterCrashDumpFileEnd:
- if (!KSUCCESS(Status)) {
- if (NewCrashDumpFile != NULL) {
- KepDestroyCrashDumpFile(NewCrashDumpFile);
- }
- if (BlockInformation != NULL) {
- IoDestroyFileBlockInformation(BlockInformation);
- }
- }
- return;
- }
- KSTATUS
- KepInitializeCrashDumpSupport (
- VOID
- )
- /*++
- Routine Description:
- This routine initializes system crash dump support.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- ULONG IoBufferFlags;
- ULONG PageSize;
- INITIALIZE_LIST_HEAD(&KeCrashDumpListHead);
- KeInitializeSpinLock(&KeCrashDumpListLock);
- PageSize = MmPageSize();
- ASSERT(PageSize >= sizeof(CRASH_DUMP_HEADER));
- IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS;
- KeCrashDumpScratchBuffer = MmAllocateNonPagedIoBuffer(0,
- MAX_ULONGLONG,
- 0,
- PageSize,
- IoBufferFlags);
- if (KeCrashDumpScratchBuffer == NULL) {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- return STATUS_SUCCESS;
- }
- KSTATUS
- KepWriteCrashDump (
- ULONG CrashCode,
- ULONGLONG Parameter1,
- ULONGLONG Parameter2,
- ULONGLONG Parameter3,
- ULONGLONG Parameter4
- )
- /*++
- Routine Description:
- This routine writes crash dump data to disk.
- Arguments:
- CrashCode - Supplies the reason for the system crash.
- Parameter1 - Supplies an optional parameter regarding the crash.
- Parameter2 - Supplies an optional parameter regarding the crash.
- Parameter3 - Supplies an optional parameter regarding the crash.
- Parameter4 - Supplies an optional parameter regarding the crash.
- Return Value:
- Status code.
- --*/
- {
- PFILE_BLOCK_IO_CONTEXT BlockIoContext;
- PDISK_BLOCK_IO_RESET BlockIoReset;
- PVOID Buffer;
- ULONG BufferSize;
- UINTN BytesCompleted;
- USHORT Checksum;
- PCRASH_DUMP_FILE CrashFile;
- PLIST_ENTRY CurrentEntry;
- PCRASH_DUMP_HEADER Header;
- KSTATUS Status;
- SYSTEM_VERSION_INFORMATION VersionInformation;
- if (LIST_EMPTY(&KeCrashDumpListHead)) {
- RtlDebugPrint("No registered crash dump files.\n");
- }
- Status = STATUS_SUCCESS;
- CurrentEntry = KeCrashDumpListHead.Next;
- while (CurrentEntry != &KeCrashDumpListHead) {
- CrashFile = LIST_VALUE(CurrentEntry, CRASH_DUMP_FILE, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // If there's no interface for this one, skip it.
- //
- if (CrashFile->DiskInterface == NULL) {
- RtlDebugPrint("Skipping dump file 0x%x without interface.\n",
- CrashFile);
- continue;
- }
- //
- // The file should be big enough for the minimal dump.
- //
- ASSERT(CrashFile->FileSize >= sizeof(CRASH_DUMP_HEADER));
- //
- // Reset the disk for the crash dump, giving the device a heads up that
- // writes are about to come in.
- //
- BlockIoContext = &(CrashFile->BlockIoContext);
- BlockIoReset = BlockIoContext->BlockIoReset;
- if (BlockIoReset != NULL) {
- Status = BlockIoReset(CrashFile->BlockIoContext.DiskToken);
- if (!KSUCCESS(Status)) {
- goto WriteCrashDumpEnd;
- }
- }
- //
- // Copy the crash dump header to the scratch buffer.
- //
- ASSERT(KeCrashDumpScratchBuffer->FragmentCount == 1);
- ASSERT(KeCrashDumpScratchBuffer->Fragment[0].VirtualAddress != NULL);
- Header = KeCrashDumpScratchBuffer->Fragment[0].VirtualAddress;
- RtlZeroMemory(Header, sizeof(CRASH_DUMP_HEADER));
- Header->Signature = CRASH_DUMP_SIGNATURE;
- Header->Type = CrashDumpMinimal;
- Header->DumpSize = sizeof(CRASH_DUMP_HEADER);
- Header->CrashCode = CrashCode;
- Header->Parameter1 = Parameter1;
- Header->Parameter2 = Parameter2;
- Header->Parameter3 = Parameter3;
- Header->Parameter4 = Parameter4;
- //
- // Copy the system version information to the header if available.
- //
- Buffer = (PVOID)(Header + 1);
- BufferSize = KeCrashDumpScratchBuffer->Fragment[0].Size -
- sizeof(CRASH_DUMP_HEADER);
- Status = KeGetSystemVersion(&VersionInformation, Buffer, &BufferSize);
- if (KSUCCESS(Status)) {
- Header->MajorVersion = VersionInformation.MajorVersion;
- Header->MinorVersion = VersionInformation.MinorVersion;
- Header->Revision = VersionInformation.Revision;
- Header->SerialVersion = VersionInformation.SerialVersion;
- Header->ReleaseLevel = VersionInformation.ReleaseLevel;
- Header->DebugLevel = VersionInformation.DebugLevel;
- if (VersionInformation.ProductName != NULL) {
- Header->ProductNameOffset =
- (PVOID)VersionInformation.ProductName - (PVOID)Header;
- }
- if (VersionInformation.BuildString != NULL) {
- Header->BuildStringOffset =
- (PVOID)VersionInformation.BuildString - (PVOID)Header;
- }
- RtlCopyMemory(&(Header->BuildTime),
- &(VersionInformation.BuildTime),
- sizeof(SYSTEM_TIME));
- Header->DumpSize += BufferSize;
- }
- //
- // Calculate the header's checksum. Do not include the product and
- // build strings as they are outside the header.
- //
- Checksum = KepCalculateChecksum(Header, sizeof(CRASH_DUMP_HEADER));
- Header->HeaderChecksum = Checksum;
- //
- // Write the header out to the file.
- //
- Status = IoWriteFileBlocks(&(CrashFile->BlockIoContext),
- KeCrashDumpScratchBuffer,
- 0,
- (UINTN)Header->DumpSize,
- &BytesCompleted);
- if (!KSUCCESS(Status)) {
- RtlDebugPrint("Failed to write crash dump to file 0x%x: %d\n",
- CrashFile,
- Status);
- continue;
- }
- //
- // One crash dump file was successfully written. If that's all that's
- // requested, stop now.
- //
- if (KeWriteAllCrashDumpFiles == FALSE) {
- break;
- }
- }
- WriteCrashDumpEnd:
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- KepCrashDumpDiskInterfaceNotificationCallback (
- PVOID Context,
- PDEVICE Device,
- PVOID InterfaceBuffer,
- ULONG InterfaceBufferSize,
- BOOL Arrival
- )
- /*++
- Routine Description:
- This routine is called when the disk interface associated with a crash
- dump file appears or disappears.
- Arguments:
- Context - Supplies the caller's context pointer, supplied when the caller
- requested interface notifications.
- Device - Supplies a pointer to the device exposing or deleting the
- interface.
- InterfaceBuffer - Supplies a pointer to the interface buffer of the
- interface.
- InterfaceBufferSize - Supplies the buffer size.
- Arrival - Supplies TRUE if a new interface is arriving, or FALSE if an
- interface is departing.
- Return Value:
- None.
- --*/
- {
- PFILE_BLOCK_IO_CONTEXT BlockIoContext;
- PDISK_INTERFACE DiskInterface;
- PCRASH_DUMP_FILE DumpFile;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- DumpFile = Context;
- BlockIoContext = &(DumpFile->BlockIoContext);
- if (Arrival != FALSE) {
- DiskInterface = InterfaceBuffer;
- if ((InterfaceBufferSize < sizeof(DISK_INTERFACE)) ||
- (DiskInterface->Version < DISK_INTERFACE_VERSION)) {
- return;
- }
- //
- // It's not expected that the device would expose multiple disk
- // interfaces.
- //
- ASSERT(DumpFile->DiskInterface == NULL);
- DumpFile->DiskInterface = InterfaceBuffer;
- BlockIoContext->DiskToken = DiskInterface->DiskToken;
- BlockIoContext->BlockSize = DiskInterface->BlockSize;
- BlockIoContext->BlockCount = DiskInterface->BlockCount;
- BlockIoContext->BlockIoReset = DiskInterface->BlockIoReset;
- BlockIoContext->BlockIoRead = DiskInterface->BlockIoRead;
- BlockIoContext->BlockIoWrite = DiskInterface->BlockIoWrite;
- //
- // The interface is disappearing.
- //
- } else {
- DumpFile->DiskInterface = NULL;
- BlockIoContext->BlockIoRead = NULL;
- BlockIoContext->BlockIoWrite = NULL;
- BlockIoContext->BlockIoReset = NULL;
- }
- return;
- }
- VOID
- KepDestroyCrashDumpFile (
- PCRASH_DUMP_FILE CrashDumpFile
- )
- /*++
- Routine Description:
- This routine destroys a crash dump file.
- Arguments:
- CrashDumpFile - Supplies a pointer to the crash dump file to destroy.
- Return Value:
- None.
- --*/
- {
- PFILE_BLOCK_IO_CONTEXT BlockIoContext;
- KSTATUS Status;
- BlockIoContext = &(CrashDumpFile->BlockIoContext);
- if (BlockIoContext->FileBlockInformation != NULL) {
- IoDestroyFileBlockInformation(BlockIoContext->FileBlockInformation);
- }
- Status = IoUnregisterForInterfaceNotifications(
- &KeDiskInterfaceUuid,
- KepCrashDumpDiskInterfaceNotificationCallback,
- CrashDumpFile->Device,
- CrashDumpFile);
- if (!KSUCCESS(Status)) {
- ASSERT(FALSE);
- return;
- }
- MmFreeNonPagedPool(CrashDumpFile);
- return;
- }
- USHORT
- KepCalculateChecksum (
- PVOID Data,
- ULONG DataSize
- )
- /*++
- Routine Description:
- This routine calculates the one's compliment checksum of a data buffer.
- Arguments:
- Data - Supplies a pointer to the data on which to calculate the checksum.
- DataSize - Supplies the size of the data, in bytes.
- Return Value:
- Returns thee one's compliment checksum of the data.
- --*/
- {
- ULONG ShortIndex;
- PUSHORT ShortPointer;
- ULONG Sum;
- Sum = 0;
- //
- // Now checksum the actual header and data.
- //
- ShortPointer = (PUSHORT)Data;
- for (ShortIndex = 0;
- ShortIndex < DataSize / sizeof(USHORT);
- ShortIndex += 1) {
- Sum += ShortPointer[ShortIndex];
- }
- //
- // If the data size is odd, then grab the last byte.
- //
- if ((DataSize & 0x1) != 0) {
- Sum += *((PUCHAR)&(ShortPointer[ShortIndex]));
- }
- //
- // With one's complement arithmetic, every time a wraparound occurs the
- // carry must be added back in on the right (to skip over "negative zero").
- // Perform all these carries at once by adding in the high word. That
- // addition itself can also cause a wraparound, which is why the while loop
- // is there.
- //
- while ((Sum >> (sizeof(USHORT) * BITS_PER_BYTE)) != 0) {
- Sum = (Sum & 0xFFFF) + (Sum >> (sizeof(USHORT) * BITS_PER_BYTE));
- }
- //
- // The checksum is the one's complement of the sum.
- //
- return (USHORT)~Sum;
- }
|