You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
8.9 KiB
389 lines
8.9 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|