|
|
/*++
Copyright (c) 2002 Microsoft Corporation
Module Name:
bugcheck.c
Abstract:
Port library routines for handling bugcheck callbacks.
Author:
Matthew D Hendel (math) 4-April-2002
Revision History:
--*/
#include "precomp.h"
//
// Definitions
//
#define PORT_BUGCHECK_TAG ('dBlP')
//
// Internal structures.
//
typedef struct _PORT_BUGCHECK_DATA { PVOID Buffer; ULONG BufferLength; ULONG BufferUsed; GUID Guid; PPORT_BUGCHECK_CALLBACK_ROUTINE CallbackRoutine; KBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord; KBUGCHECK_REASON_CALLBACK_RECORD SecondaryCallbackRecord; } PORT_BUGCHECK_DATA, *PPORT_BUGCHECK_DATA;
//
// Global Variables
//
PPORT_BUGCHECK_DATA PortBugcheckData;
//
// Imports
//
extern PULONG_PTR KiBugCheckData;
//
// Prototypes
//
VOID PortBugcheckGatherDataCallback( IN KBUGCHECK_CALLBACK_REASON Reason, IN PKBUGCHECK_REASON_CALLBACK_RECORD Record, IN OUT PVOID ReasonSpecificData, IN ULONG ReasonSpecificDataLength );
VOID PortBugcheckWriteDataCallback( IN KBUGCHECK_CALLBACK_REASON Reason, IN PKBUGCHECK_REASON_CALLBACK_RECORD Record, IN OUT PVOID ReasonSpecificData, IN ULONG ReasonSpecificDataLength );
//
// Implementation
//
NTSTATUS PortRegisterBugcheckCallback( IN PCGUID BugcheckDataGuid, IN PPORT_BUGCHECK_CALLBACK_ROUTINE BugcheckRoutine ) /*++
Routine Description:
Register a bugcheck callback routine.
Arguments:
BugcheckDataGuid - A GUID used to identify the data in the dump.
BugcheckRoutine - Routine called when a bugcheck occurs.
Return Value:
NTSTATUS code.
--*/ { BOOLEAN Succ; NTSTATUS Status; PVOID Temp; PPORT_BUGCHECK_DATA BugcheckData; PVOID Buffer;
ASSERT (KeGetCurrentIrql() <= DISPATCH_LEVEL);
Status = STATUS_SUCCESS;
BugcheckData = ExAllocatePoolWithTag (NonPagedPool, sizeof (PORT_BUGCHECK_DATA), PORT_BUGCHECK_TAG);
if (BugcheckData == NULL) { Status = STATUS_NO_MEMORY; goto done; }
RtlZeroMemory (BugcheckData, sizeof (PORT_BUGCHECK_DATA));
Buffer = ExAllocatePoolWithTag (NonPagedPool, PAGE_SIZE, PORT_BUGCHECK_TAG);
if (Buffer == NULL) { Status = STATUS_NO_MEMORY; goto done; }
BugcheckData->Buffer = Buffer; BugcheckData->BufferLength = 2 * PAGE_SIZE; BugcheckData->CallbackRoutine = BugcheckRoutine; BugcheckData->Guid = *BugcheckDataGuid;
//
// If PortBugcheckData == NULL, swap the values, otherwise fail the
// function.
//
Temp = InterlockedCompareExchangePointer (&PortBugcheckData, BugcheckData, NULL);
if (Temp != NULL) { Status = STATUS_UNSUCCESSFUL; goto done; }
KeInitializeCallbackRecord (&BugcheckData->CallbackRecord); KeInitializeCallbackRecord (&BugcheckData->SecondaryCallbackRecord);
//
// This registers the bugcheck "gather data" function.
//
Succ = KeRegisterBugCheckReasonCallback (&BugcheckData->CallbackRecord, PortBugcheckGatherDataCallback, KbCallbackReserved1, "PL");
if (!Succ) { Status = STATUS_UNSUCCESSFUL; goto done; }
Succ = KeRegisterBugCheckReasonCallback (&BugcheckData->SecondaryCallbackRecord, PortBugcheckWriteDataCallback, KbCallbackSecondaryDumpData, "PL");
if (!Succ) { Status = STATUS_UNSUCCESSFUL; goto done; }
done:
if (!NT_SUCCESS (Status)) { PortDeregisterBugcheckCallback (BugcheckDataGuid); }
return STATUS_SUCCESS; }
NTSTATUS PortDeregisterBugcheckCallback( IN PCGUID BugcheckDataGuid ) /*++
Routine Description:
Deregister a bugcheck callback routine previously registered by PortRegisterBugcheckCallback.
Arguments:
BugcheckDataGuid - Guid associated with the data stream to deregister.
Return Value:
NTSTATUS code.
--*/ { PPORT_BUGCHECK_DATA BugcheckData; BugcheckData = InterlockedExchangePointer (&PortBugcheckData, NULL);
if (BugcheckData == NULL || !IsEqualGUID (&BugcheckData->Guid, BugcheckDataGuid)) {
return STATUS_UNSUCCESSFUL; }
KeDeregisterBugCheckReasonCallback (&BugcheckData->SecondaryCallbackRecord); KeDeregisterBugCheckReasonCallback (&BugcheckData->CallbackRecord);
if (BugcheckData->Buffer != NULL) { ExFreePoolWithTag (BugcheckData->Buffer, PORT_BUGCHECK_TAG); BugcheckData->Buffer = NULL; } ExFreePoolWithTag (BugcheckData, PORT_BUGCHECK_TAG);
return STATUS_SUCCESS; }
VOID PortBugcheckGatherDataCallback( IN KBUGCHECK_CALLBACK_REASON Reason, IN PKBUGCHECK_REASON_CALLBACK_RECORD Record, IN OUT PVOID ReasonSpecificData, IN ULONG ReasonSpecificDataLength ) /*++
Routine Description:
Port driver routine to gather data during a bugcheck.
Arguments:
Reason - Must be KbCallbackReserved1.
Record - Supplies the bugcheck record previously registered.
ReasonSpecificData - Not used.
ReasonSpecificDataLength - Not used.
Return Value:
None.
Environment:
The routine is called from bugcheck context: at HIGH_LEVEL, with interrupts disabled and other processors stalled. --*/ { NTSTATUS Status; KBUGCHECK_DATA BugcheckData; //
// On multiproc we only go till IPI_LEVEL
//
ASSERT (KeGetCurrentIrql() >= IPI_LEVEL); ASSERT (Reason == KbCallbackReserved1); ASSERT (PortBugcheckData != NULL); ASSERT (PortBugcheckData->BufferUsed == 0);
BugcheckData.BugCheckCode = (ULONG)KiBugCheckData[0]; BugcheckData.BugCheckParameter1 = KiBugCheckData[1]; BugcheckData.BugCheckParameter2 = KiBugCheckData[2]; BugcheckData.BugCheckParameter3 = KiBugCheckData[3]; BugcheckData.BugCheckParameter4 = KiBugCheckData[4]; //
// Gather data, put it in the buffer.
//
Status = PortBugcheckData->CallbackRoutine (&BugcheckData, PortBugcheckData->Buffer, PortBugcheckData->BufferLength, &PortBugcheckData->BufferUsed);
if (!NT_SUCCESS (Status)) { PortBugcheckData->BufferUsed = 0; } }
VOID PortBugcheckWriteDataCallback( IN KBUGCHECK_CALLBACK_REASON Reason, IN PKBUGCHECK_REASON_CALLBACK_RECORD Record, IN OUT PVOID ReasonSpecificData, IN ULONG ReasonSpecificDataLength ) /*++
Routine Description:
Port driver routine to write data out during a bugcheck.
Arguments:
Reason - Must be KbCallbackSecondaryData.
Record - Supplies the bugcheck record previously registered.
ReasonSpecificData - Pointer to KBUGCHECK_SECONDARY_DUMP_DATA structure.
ReasonSpecificDataLength - Sizeof ReasonSpecificData buffer.
Return Value:
None.
Environment:
The routine is called from bugcheck context: at HIGH_LEVEL, with interrupts disabled and other processors stalled. --*/ { PKBUGCHECK_SECONDARY_DUMP_DATA SecondaryData; //
// On multiproc we only go till IPI_LEVEL
//
ASSERT (KeGetCurrentIrql() >= IPI_LEVEL); ASSERT (ReasonSpecificDataLength >= sizeof(KBUGCHECK_SECONDARY_DUMP_DATA)); ASSERT (Reason == KbCallbackSecondaryDumpData);
SecondaryData = (PKBUGCHECK_SECONDARY_DUMP_DATA)ReasonSpecificData;
//
// This means we have no data to provide.
//
if (PortBugcheckData->BufferUsed == 0) { return ; } //
// If OutBuffer is NULL, then this is a request for sizing information
// only. Do not fill in the rest of the data.
//
if (SecondaryData->OutBuffer == NULL) { SecondaryData->Guid = PortBugcheckData->Guid; SecondaryData->OutBuffer = PortBugcheckData->Buffer; SecondaryData->OutBufferLength = PortBugcheckData->BufferUsed; return ; }
//
// Is there enough space?
//
if (SecondaryData->MaximumAllowed < PortBugcheckData->BufferUsed) { return ; }
SecondaryData->Guid = PortBugcheckData->Guid; SecondaryData->OutBuffer = PortBugcheckData->Buffer; SecondaryData->OutBufferLength = PortBugcheckData->BufferUsed; }
|