Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3517 lines
131 KiB

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
Mca.c
Abstract:
Machine Check Architecture interface
Author:
AlanWar
Environment:
Kernel mode
Revision History:
--*/
#pragma warning(disable:4206) // translation unit empty
#include "wmikmp.h"
#include <mce.h>
#include "hal.h"
#include "ntiologc.h"
#define MCA_EVENT_INSTANCE_NAME L"McaEvent"
#define MCA_UNDEFINED_CPU 0xffffffff
#if defined(_IA64_)
#define SAL_30_ERROR_REVISION 0x0002
#define HalpGetFwMceLogProcessorNumber( /* PERROR_RECORD_HEADER */ _Log ) \
((UCHAR) (_Log)->TimeStamp.Reserved )
#endif
#if defined(_X86_) || defined(_AMD64_)
#define HalpGetFwMceLogProcessorNumber( /* PMCA_EXCEPTION */ _Log ) \
( (_Log)->ProcessorNumber )
typedef MCA_EXCEPTION ERROR_LOGRECORD, *PERROR_LOGRECORD;
typedef MCA_EXCEPTION ERROR_RECORD_HEADER, *PERROR_RECORD_HEADER;
#endif
//
// Types of corrected errors that are tracked
//
typedef enum
{
SingleBitEcc,
CpuCache,
CpuTlb,
CpuBus,
CpuRegFile
} MCECORRECTEDTYPE, *PMCECORRECTEDTYPE;
typedef struct
{
LIST_ENTRY List;
MCECORRECTEDTYPE Type;
USHORT Counter;
USHORT Flags;
LARGE_INTEGER Timestamp;
union
{
//
// For SingleBitEcc type, indicates physical address of page
// where error occured
//
PHYSICAL_ADDRESS SingleBitEccAddress;
//
// For Cpu* types, indicates cpu on which the error
// occured
//
ULONG CpuId;
};
} MCECORRECTEDEVENT, *PMCECORRECTEDEVENT;
#define CORRECTED_MCE_EVENT_BUSY 0x0001
BOOLEAN WmipMceEventDelivery(
IN PVOID Reserved,
IN KERNEL_MCE_DELIVERY_OPERATION Operation,
IN PVOID Argument2
);
BOOLEAN WmipMceDelivery(
IN PVOID Reserved,
IN KERNEL_MCE_DELIVERY_OPERATION Operation,
IN PVOID Argument2
);
void WmipMceWorkerRoutine(
IN PVOID Context // Not Used
);
NTSTATUS WmipGetLogFromHal(
HAL_QUERY_INFORMATION_CLASS InfoClass,
PVOID Token,
PWNODE_SINGLE_INSTANCE *Wnode,
PERROR_LOGRECORD *Mca,
PULONG McaSize,
ULONG MaxSize,
LPGUID Guid
);
NTSTATUS WmipRegisterMcaHandler(
ULONG Phase
);
NTSTATUS WmipBuildMcaCmcEvent(
OUT PWNODE_SINGLE_INSTANCE Wnode,
IN LPGUID EventGuid,
IN PERROR_LOGRECORD McaCmcEvent,
IN ULONG McaCmcSize
);
NTSTATUS WmipGetRawMCAInfo(
OUT PUCHAR Buffer,
IN OUT PULONG BufferSize
);
NTSTATUS WmipWriteMCAEventLogEvent(
PUCHAR Event
);
NTSTATUS WmipSetupWaitForWbem(
void
);
void WmipIsWbemRunningDispatch(
IN PKDPC Dpc,
IN PVOID DeferredContext, // Not Used
IN PVOID SystemArgument1, // Not Used
IN PVOID SystemArgument2 // Not Used
);
void WmipPollingDpcRoutine(
IN PKDPC Dpc,
IN PVOID DeferredContext, // MCEQUERYINFO
IN PVOID SystemArgument1, // New polling interval
IN PVOID SystemArgument2 // Not used
);
void WmipIsWbemRunningWorker(
PVOID Context
);
BOOLEAN WmipCheckIsWbemRunning(
void
);
void WmipProcessPrevMcaLogs(
void
);
void WmipFreeCorrectedMCEEvent(
PMCECORRECTEDEVENT Event
);
PMCECORRECTEDEVENT WmipAllocCorrectedMCEEvent(
MCECORRECTEDTYPE Type
);
NTSTATUS WmipTrackCorrectedMCE(
IN MCECORRECTEDTYPE Type,
IN PERROR_RECORD_HEADER Record,
#if defined(_IA64_)
IN PERROR_SECTION_HEADER Section,
#endif
OUT ULONG *LogToEventlog
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,WmipAllocCorrectedMCEEvent)
#pragma alloc_text(PAGE,WmipFreeCorrectedMCEEvent)
#pragma alloc_text(PAGE,WmipTrackCorrectedMCE)
#pragma alloc_text(PAGE,WmipRegisterMcaHandler)
#pragma alloc_text(PAGE,WmipMceWorkerRoutine)
#pragma alloc_text(PAGE,WmipGetLogFromHal)
#pragma alloc_text(PAGE,WmipBuildMcaCmcEvent)
#pragma alloc_text(PAGE,WmipGetRawMCAInfo)
#pragma alloc_text(PAGE,WmipWriteMCAEventLogEvent)
#pragma alloc_text(PAGE,WmipGenerateMCAEventlog)
#pragma alloc_text(PAGE,WmipIsWbemRunningWorker)
#pragma alloc_text(PAGE,WmipCheckIsWbemRunning)
#pragma alloc_text(PAGE,WmipSetupWaitForWbem)
#pragma alloc_text(PAGE,WmipProcessPrevMcaLogs)
#endif
//
// Set to TRUE when the registry indicates that popups should be
// disabled. HKLM\System\CurrentControlSet\Control\WMI\DisableMCAPopups
//
ULONG WmipDisableMCAPopups;
//
// Guids for the various RAW MCA/CMC/CPE events
//
GUID WmipMSMCAEvent_CPUErrorGuid = MSMCAEvent_CPUErrorGuid;
GUID WmipMSMCAEvent_MemoryErrorGuid = MSMCAEvent_MemoryErrorGuid;
GUID WmipMSMCAEvent_PCIBusErrorGuid = MSMCAEvent_PCIBusErrorGuid;
GUID WmipMSMCAEvent_PCIComponentErrorGuid = MSMCAEvent_PCIComponentErrorGuid;
GUID WmipMSMCAEvent_SystemEventErrorGuid = MSMCAEvent_SystemEventErrorGuid;
GUID WmipMSMCAEvent_SMBIOSErrorGuid = MSMCAEvent_SMBIOSErrorGuid;
GUID WmipMSMCAEvent_PlatformSpecificErrorGuid = MSMCAEvent_PlatformSpecificErrorGuid;
GUID WmipMSMCAEvent_InvalidErrorGuid = MSMCAEvent_InvalidErrorGuid;
GUID WmipMSMCAEvent_MemoryPageRemoved = MSMCAEvent_MemoryPageRemovedGuid;
//
// GUIDs for the different error sections within a MCA
//
#if defined(_IA64_)
GUID WmipErrorProcessorGuid = ERROR_PROCESSOR_GUID;
GUID WmipErrorMemoryGuid = ERROR_MEMORY_GUID;
GUID WmipErrorPCIBusGuid = ERROR_PCI_BUS_GUID;
GUID WmipErrorPCIComponentGuid = ERROR_PCI_COMPONENT_GUID;
GUID WmipErrorSELGuid = ERROR_SYSTEM_EVENT_LOG_GUID;
GUID WmipErrorSMBIOSGuid = ERROR_SMBIOS_GUID;
GUID WmipErrorSpecificGuid = ERROR_PLATFORM_SPECIFIC_GUID;
#endif
//
// Each type of MCE has a control structure that is used to determine
// whether to poll or wait for an interrupt to determine when to query
// for the logs. This is needed since we can get a callback from the
// HAL at high IRQL to inform us that a MCE log is available.
// Additionally Ke Timer used for polling will calls us at DPC level.
// So in the case of an interrupt we will queue a DPC. Within the DPC
// routine we will queue a work item so that we can get back to
// passive level and be able to call the hal to get the logs (Can only
// call hal at passive). The DPC and work item routines are common so a
// MCEQUERYINFO struct is passed around so that it can operate on the
// correct log type. Note that this implies that there may be multiple
// work items querying the hal for different log types at the same
// time. In addition this struct also contains useful log related
// information including the maximum log size (as reported by the HAL),
// the token that must be passed to the HAL when querying for the
// logs and the HAL InfoClass to use when querying for the logs.
//
// PollFrequency keeps track of the number of seconds before initiating a
// query. If it is 0 (HAL_CPE_DISABLED / HAL_CMC_DISABLED) then no
// polling occurs and if it is -1 (HAL_CPE_INTERRUPTS_BASED /
// HAL_CMC_INTERRUPTS_BASED) then no polling occurs either. There is
// only one work item active for each log type and this is enforced via
// ItemsOutstanding in that only whenever it transitions from 0 to 1 is
// the work item queued.
//
#define DEFAULT_MAX_MCA_SIZE 0x1000
#define DEFAULT_MAX_CMC_SIZE 0x1000
#define DEFAULT_MAX_CPE_SIZE 0x1000
typedef struct
{
HAL_QUERY_INFORMATION_CLASS InfoClass; // HAL Info class to use in MCE query
ULONG PollFrequency; // Polling Frequency in seconds
PVOID Token; // HAL Token to use in MCE Queries
LONG ItemsOutstanding; // Number of interrupts or poll requests to process
ULONG MaxSize; // Max size for log (as reported by HAL)
GUID WnodeGuid; // GUID to use for the raw data event
GUID SwitchToPollGuid; // GUID to use to fire event for switching to polled mode
NTSTATUS SwitchToPollErrorCode; // Eventlog error code that indicates a switch to polled mode
ULONG WorkerInProgress; // Set to 1 if worker routine is running
KSPIN_LOCK DpcLock;
KDPC DeliveryDpc; // DPC to handle delivery
KTIMER PollingTimer; // KTIMER used for polling
KDPC PollingDpc; // DPC to use for polling
WORK_QUEUE_ITEM WorkItem; // Work item used to query for log
} MCEQUERYINFO, *PMCEQUERYINFO;
MCEQUERYINFO WmipMcaQueryInfo =
{
HalMcaLogInformation,
HAL_MCA_INTERRUPTS_BASED, // Corrected MCA are delivered by interrupts
NULL,
0,
DEFAULT_MAX_MCA_SIZE,
MSMCAInfo_RawMCAEventGuid
};
MCEQUERYINFO WmipCmcQueryInfo =
{
HalCmcLogInformation,
HAL_CMC_DISABLED,
NULL,
0,
DEFAULT_MAX_CMC_SIZE,
MSMCAInfo_RawCMCEventGuid,
MSMCAEvent_SwitchToCMCPollingGuid,
MCA_WARNING_CMC_THRESHOLD_EXCEEDED,
0
};
MCEQUERYINFO WmipCpeQueryInfo =
{
HalCpeLogInformation,
HAL_CPE_DISABLED,
NULL,
0,
DEFAULT_MAX_CPE_SIZE,
MSMCAInfo_RawCorrectedPlatformEventGuid,
MSMCAEvent_SwitchToCPEPollingGuid,
MCA_WARNING_CPE_THRESHOLD_EXCEEDED,
0
};
//
// Used for waiting until WBEM is ready to receive events
//
KTIMER WmipIsWbemRunningTimer;
KDPC WmipIsWbemRunningDpc;
WORK_QUEUE_ITEM WmipIsWbemRunningWorkItem;
LIST_ENTRY WmipWaitingMCAEvents = {&WmipWaitingMCAEvents, &WmipWaitingMCAEvents};
#define WBEM_STATUS_UNKNOWN 0 // Polling process for waiting is not started
#define WBEM_IS_RUNNING 1 // WBEM is currently running
#define WAITING_FOR_WBEM 2 // Polling process for waiting is started
UCHAR WmipIsWbemRunningFlag;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
//
// MCA information obtained at boot and holds the MCA that caused the
// system to bugcheck on the previous boot
//
ULONG WmipRawMCASize;
PMSMCAInfo_RawMCAData WmipRawMCA;
//
// Status of the MCE registration process
//
#define MCE_STATE_UNINIT 0
#define MCE_STATE_REGISTERED 1
#define MCE_STATE_RUNNING 2
#define MCE_STATE_ERROR -1
ULONG WmipMCEState;
//
// Configurable paramters for managing thresholds for eventlog
// suppression and recovery action for corrected MCE
//
//
// Interval within which multiple identical errors will be reported as
// a single error to the system eventlog. Can be configured under
// HKLM\System\CurrentControlSet\Control\WMI\CoalesceCorrectedErrorInterval
// A value of 0 will cause no coalesce of identical errors
//
ULONG WmipCoalesceCorrectedErrorInterval = 5000;
//
// Number of single bit ecc errors that can occur in the same page
// before it is attempted to map out the page. Can be configured under :
// HKLM\System\CurrentControlSet\Control\WMI\SingleBitEccErrorThreshold
// A value of 0 will cause no attempt to map out pages
//
ULONG WmipSingleBitEccErrorThreshold = 6;
//
// Maxiumum number of MCE events being tracked at one time. If there is
// more than this limit then the oldest ones are recycled. Can be
// configured under :
// HKLM\System\CurrentControlSet\Control\WMI\MaxCorrectedMCEOutstanding
// A value of 0 will disable tracking of corrected errors
//
ULONG WmipMaxCorrectedMCEOutstanding = 5;
//
// List of corrected MCE that are being tracked
//
LIST_ENTRY WmipCorrectedMCEHead = {&WmipCorrectedMCEHead, &WmipCorrectedMCEHead};
ULONG WmipCorrectedMCECount;
//
// Counter of maximum eventlog entries generated by any source. Can be
// configured under:
// HKLM\System\CurrentControlSet\Control\WMI\MaxCorrectedEventlogs
//
ULONG WmipCorrectedEventlogCounter = 20;
//
// Check if WBEM is already running and if not check if we've already
// kicked off the timer that will wait for wbem to start
//
#define WmipIsWbemRunning() ((WmipIsWbemRunningFlag == WBEM_IS_RUNNING) ? \
TRUE : \
FALSE)
void WmipInsertQueueMCEDpc(
PMCEQUERYINFO QueryInfo
);
NTSTATUS WmipWriteToEventlog(
NTSTATUS ErrorCode,
NTSTATUS FinalStatus
)
{
PIO_ERROR_LOG_PACKET ErrLog;
NTSTATUS Status;
ErrLog = IoAllocateErrorLogEntry(WmipServiceDeviceObject,
sizeof(IO_ERROR_LOG_PACKET));
if (ErrLog != NULL) {
//
// Fill it in and write it out as a single string.
//
ErrLog->ErrorCode = ErrorCode;
ErrLog->FinalStatus = FinalStatus;
ErrLog->StringOffset = 0;
ErrLog->NumberOfStrings = 0;
IoWriteErrorLogEntry(ErrLog);
Status = STATUS_SUCCESS;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return(Status);
}
NTSTATUS WmipFireOffWmiEvent(
LPGUID Guid,
ULONG DataSize,
PVOID DataPtr
)
{
PVOID Ptr;
PWNODE_SINGLE_INSTANCE Wnode;
PWCHAR Wptr;
ULONG RoundedDataSize;
NTSTATUS Status;
RoundedDataSize = (DataSize + 1) & ~1;
Wnode = ExAllocatePoolWithTag(NonPagedPool,
sizeof(WNODE_SINGLE_INSTANCE) +
RoundedDataSize +
sizeof(USHORT) +
sizeof(MCA_EVENT_INSTANCE_NAME),
WmipMCAPoolTag);
if (Wnode != NULL)
{
Wnode->WnodeHeader.BufferSize = sizeof(WNODE_SINGLE_INSTANCE) +
sizeof(USHORT) +
RoundedDataSize +
sizeof(MCA_EVENT_INSTANCE_NAME);
Wnode->WnodeHeader.Guid = *Guid;
Wnode->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
WNODE_FLAG_EVENT_ITEM;
KeQuerySystemTime(&Wnode->WnodeHeader.TimeStamp);
Wnode->DataBlockOffset = sizeof(WNODE_SINGLE_INSTANCE);
Wnode->SizeDataBlock = DataSize;
if (DataPtr != NULL)
{
Ptr = OffsetToPtr(Wnode, Wnode->DataBlockOffset);
memcpy(Ptr, DataPtr, DataSize);
}
Wnode->OffsetInstanceName = sizeof(WNODE_SINGLE_INSTANCE) + RoundedDataSize;
Wptr = (PWCHAR)OffsetToPtr(Wnode, Wnode->OffsetInstanceName);
*Wptr++ = sizeof(MCA_EVENT_INSTANCE_NAME);
RtlCopyMemory(Wptr,
MCA_EVENT_INSTANCE_NAME,
sizeof(MCA_EVENT_INSTANCE_NAME));
Status = IoWMIWriteEvent(Wnode);
if (! NT_SUCCESS(Status))
{
ExFreePool(Wnode);
}
}
else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return(Status);
}
NTSTATUS WmipBuildMcaCmcEvent(
OUT PWNODE_SINGLE_INSTANCE Wnode,
IN LPGUID EventGuid,
IN PERROR_LOGRECORD McaCmcEvent,
IN ULONG McaCmcSize
)
/*++
Routine Description:
This routine will take a MCA or CMC log and build a
WNODE_EVENT_ITEM for it.
This routine may be called at DPC
Arguments:
Wnode is the wnode buffer in which to build the event
EventGuid is the guid to use in the event wnode
McaCmcEvent is the MCA, CMC or CPE data payload to put into the
event
McaCmcSize is the size of the event data
Return Value:
NT status code
--*/
{
PMSMCAInfo_RawCMCEvent Ptr;
ULONG Size;
PAGED_CODE();
Size = McaCmcSize + FIELD_OFFSET(MSMCAInfo_RawCMCEvent,
Records) +
FIELD_OFFSET(MSMCAInfo_Entry, Data);
RtlZeroMemory(Wnode, sizeof(WNODE_SINGLE_INSTANCE));
Wnode->WnodeHeader.BufferSize = Size + sizeof(WNODE_SINGLE_INSTANCE);
Wnode->WnodeHeader.ProviderId = IoWMIDeviceObjectToProviderId(WmipServiceDeviceObject);
KeQuerySystemTime(&Wnode->WnodeHeader.TimeStamp);
Wnode->WnodeHeader.Guid = *EventGuid;
Wnode->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
WNODE_FLAG_EVENT_ITEM |
WNODE_FLAG_STATIC_INSTANCE_NAMES;
Wnode->DataBlockOffset = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData);
Wnode->SizeDataBlock = Size;
Ptr = (PMSMCAInfo_RawCMCEvent)&Wnode->VariableData;
Ptr->Count = 1; // 1 Record in this event
Ptr->Records[0].Length = McaCmcSize; // Size of log record in bytes
if (McaCmcEvent != NULL)
{
RtlCopyMemory(Ptr->Records[0].Data, McaCmcEvent, McaCmcSize);
}
return(STATUS_SUCCESS);
}
NTSTATUS WmipQueryLogAndFireEvent(
PMCEQUERYINFO QueryInfo
)
/*++
Routine Description:
Utility routine that will query the hal for a log and then if one
is returned successfully then will fire the appropriate WMI events
Arguments:
QueryInfo is a pointer to the MCEQUERYINFO for the type of log that
needs to be queried.
Return Value:
--*/
{
PWNODE_SINGLE_INSTANCE Wnode;
NTSTATUS Status, Status2;
ULONG Size;
PERROR_LOGRECORD Log;
PAGED_CODE();
//
// Call HAL to get the log
//
Status = WmipGetLogFromHal(QueryInfo->InfoClass,
QueryInfo->Token,
&Wnode,
&Log,
&Size,
QueryInfo->MaxSize,
&QueryInfo->WnodeGuid);
if (NT_SUCCESS(Status))
{
//
// Look at the event and fire it off as WMI events that
// will generate eventlog events
//
WmipGenerateMCAEventlog((PUCHAR)Log,
Size,
FALSE);
//
// Fire the log off as a WMI event
//
Status2 = IoWMIWriteEvent(Wnode);
if (! NT_SUCCESS(Status2))
{
//
// IoWMIWriteEvent will free the wnode back to pool,
// but not if it fails
//
ExFreePool(Wnode);
}
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_MCA_LEVEL,
"WMI: MCE Event fired to WMI -> %x\n",
Status));
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_MCA_LEVEL,
"WMI: MCE Event for %p not available %x\n",
QueryInfo, Status));
}
return(Status);
}
void WmipMceWorkerRoutine(
IN PVOID Context // MCEQUERYINFO
)
/*++
Routine Description:
Worker routine that handles polling for corrected MCA, CMC and CPE
logs from the HAL and then firing them as WMI events.
Arguments:
Context is a pointer to the MCEQUERYINFO for the type of log that
needs to be queried.
Return Value:
--*/
{
PMCEQUERYINFO QueryInfo = (PMCEQUERYINFO)Context;
NTSTATUS Status;
ULONG i;
LONG x, Count;
PAGED_CODE();
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: WmipMceWorkerRoutine %p enter\n",
QueryInfo));
//
// If the worker is already in progress then we just exit
//
WmipEnterSMCritSection();
if (QueryInfo->WorkerInProgress == 0)
{
QueryInfo->WorkerInProgress = 1;
WmipLeaveSMCritSection();
} else {
WmipLeaveSMCritSection();
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: WmipMceWorkerRoutine %p in progress\n",
QueryInfo));
return;
}
//
// Check to see if access has already been disabled
//
if (QueryInfo->PollFrequency != HAL_MCE_DISABLED)
{
//
// We get all of the records by calling into the hal and querying
// for the logs until the hal returns an error or we've
// retrieved 256 records. We want to protect ourselves from the
// case where a repeated corrected error would cause the loop
// to be infinite.
//
i = 0;
do
{
//
// Remember how many corrected errors we have received up until
// this point. We guarantee that we've handled them up
// until this point
//
Count = QueryInfo->ItemsOutstanding;
Status = WmipQueryLogAndFireEvent(QueryInfo);
} while ((NT_SUCCESS(Status) && (i++ < 256)));
//
// Reset counter back to 0, but check if any errors
// had occured while we were processing. If so we go
// back and make sure they are handled. Note that this
// could cause a new worker thread to be created while we
// are still processing these, but that is ok since we only
// allow one worker thread to run at one time.
//
WmipEnterSMCritSection();
x = InterlockedExchange(&QueryInfo->ItemsOutstanding,
0);
if ((x > Count) && (i < 257))
{
//
// Since there are still more corrected errors to
// process, queue a new DPC to cause a new worker
// routine to be run.
//
WmipInsertQueueMCEDpc(QueryInfo);
}
QueryInfo->WorkerInProgress = 0;
WmipLeaveSMCritSection();
}
}
void WmipMceDispatchRoutine(
PMCEQUERYINFO QueryInfo
)
{
ULONG x;
//
// Increment the number of items that are outstanding for this info
// class. If the number of items outstanding transitions from 0 to
// 1 then this implies that a work item for this info class needs
// to be queued
//
x = InterlockedIncrement(&QueryInfo->ItemsOutstanding);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: WmipMceDispatchRoutine %p transition to %d\n",
QueryInfo,
x));
if (x == 1)
{
ExQueueWorkItem(&QueryInfo->WorkItem,
DelayedWorkQueue);
}
}
void WmipMceDpcRoutine(
IN PKDPC Dpc,
IN PVOID DeferredContext, // Not Used
IN PVOID SystemArgument1, // MCEQUERYINFO
IN PVOID SystemArgument2 // Not used
)
{
UNREFERENCED_PARAMETER (Dpc);
UNREFERENCED_PARAMETER (DeferredContext);
UNREFERENCED_PARAMETER (SystemArgument2);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: WmipMceDpcRoutine %p Enter\n",
SystemArgument1));
WmipMceDispatchRoutine((PMCEQUERYINFO)SystemArgument1);
}
void WmipPollingDpcRoutine(
IN PKDPC Dpc,
IN PVOID DeferredContext, // MCEQUERYINFO
IN PVOID SystemArgument1, // New polling Interval
IN PVOID SystemArgument2 // Not used
)
{
PMCEQUERYINFO QueryInfo = (PMCEQUERYINFO)DeferredContext;
LARGE_INTEGER li;
ULONG PollingInterval = PtrToUlong(SystemArgument1);
UNREFERENCED_PARAMETER (Dpc);
UNREFERENCED_PARAMETER (SystemArgument2);
if (QueryInfo->PollFrequency == HAL_MCE_INTERRUPTS_BASED)
{
//
// HAL has instructed us to switch into polled mode and has
// informed us of the new polling interval.
//
QueryInfo->PollFrequency = PollingInterval;
li.QuadPart = -1 * (QueryInfo->PollFrequency * 1000000000);
KeSetTimerEx(&QueryInfo->PollingTimer,
li,
QueryInfo->PollFrequency * 1000,
&QueryInfo->PollingDpc);
//
// Make a note in the eventlog that this has occured.
//
WmipWriteToEventlog(QueryInfo->SwitchToPollErrorCode,
STATUS_SUCCESS
);
//
// Inform any WMI consumers that the switch has occured
//
WmipFireOffWmiEvent(&QueryInfo->SwitchToPollGuid,
0,
NULL);
} else {
//
// Our timer fired so we need to poll
//
WmipMceDispatchRoutine(QueryInfo);
}
}
BOOLEAN WmipMceDelivery(
IN PVOID Reserved,
IN KERNEL_MCE_DELIVERY_OPERATION Operation,
IN PVOID Argument2
)
/*++
Routine Description:
This routine is called by the HAL when a CMC or CPE occurs. It is called
at high irql
Arguments:
Operation is the operation that the HAL is instructing us to do
Reserved is the CMC token
Parameter for operation specified.
For CmcSwitchToPolledMode and CpeSwitchToPolledMode, Parameter
specifies the number of seconds to between polling.
Return Value:
TRUE to indicate that we handled the delivery
--*/
{
PMCEQUERYINFO QueryInfo;
BOOLEAN ret;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MceDelivery Operation %d(%p)\n",
Operation, Argument2));
//
// First figure out which type of MCE we are dealing with
//
switch (Operation)
{
case CmcAvailable:
case CmcSwitchToPolledMode:
{
QueryInfo = &WmipCmcQueryInfo;
break;
}
case CpeAvailable:
case CpeSwitchToPolledMode:
{
QueryInfo = &WmipCpeQueryInfo;
break;
}
case McaAvailable:
{
QueryInfo = &WmipMcaQueryInfo;
break;
}
default:
{
WmipAssert(FALSE);
return(FALSE);
}
}
//
// Next determine what action to perform
//
switch (Operation)
{
case CmcAvailable:
case CpeAvailable:
case McaAvailable:
{
//
// Store the HAL token which is needed to retrieve the logs from
// the hal
//
QueryInfo->Token = Reserved;
//
// If we are ready to handle the logs and we are dealing with thse
// logs on an interrupt basis, then go ahead and queue a DPC to handle
// processing the log
//
if ((WmipMCEState == MCE_STATE_RUNNING) &&
(QueryInfo->PollFrequency == HAL_MCE_INTERRUPTS_BASED))
{
KeAcquireSpinLockAtDpcLevel(&QueryInfo->DpcLock);
KeInsertQueueDpc(&QueryInfo->DeliveryDpc,
QueryInfo,
NULL);
KeReleaseSpinLockFromDpcLevel(&QueryInfo->DpcLock);
ret = TRUE;
} else {
ret = FALSE;
}
break;
}
case CmcSwitchToPolledMode:
case CpeSwitchToPolledMode:
{
KeInsertQueueDpc(&QueryInfo->PollingDpc,
Argument2,
NULL);
ret = TRUE;
break;
}
default:
{
ret = FALSE;
break;
}
}
return(ret);
}
BOOLEAN WmipMceEventDelivery(
IN PVOID Reserved,
IN KERNEL_MCE_DELIVERY_OPERATION Operation,
IN PVOID Argument2
)
/*++
Routine Description:
This routine is called by the HAL when a situation occurs between
the HAL and SAL interface. It is called at high irql
Arguments:
Reserved has the Operation and EventType
Argument2 has the SAL return code
Return Value:
--*/
{
USHORT MceOperation;
LONGLONG SalStatus;
ULONG MceType;
PMCEQUERYINFO QueryInfo;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCEDelivery %p %d %p\n",
Reserved,
Operation,
Argument2
));
MceOperation = KERNEL_MCE_OPERATION(Reserved);
MceType = KERNEL_MCE_EVENTTYPE(Reserved);
SalStatus = (LONGLONG)Argument2;
//
// If the hal is notifying us that a GetStateInfo failed with
// SalStatus == -15 then we need to retry our query later
//
if ((MceOperation == KERNEL_MCE_OPERATION_GET_STATE_INFO) &&
(Operation == MceNotification) &&
(SalStatus == (LONGLONG)-15))
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: Sal is asking us to retry getstateinfo for type %x\n",
MceType));
switch(MceType)
{
case KERNEL_MCE_EVENTTYPE_CMC:
{
QueryInfo = &WmipCmcQueryInfo;
break;
}
case KERNEL_MCE_EVENTTYPE_CPE:
{
QueryInfo = &WmipCpeQueryInfo;
break;
}
default:
{
QueryInfo = NULL;
}
}
if (QueryInfo != NULL)
{
//
// If CMC or CPE are interrupt based then queue up a new
// DPC for performing the query. If polling based then
// there are no worries, we just wait for the next polling
// interval.
//
if ((WmipMCEState == MCE_STATE_RUNNING) &&
(QueryInfo->PollFrequency == HAL_MCE_INTERRUPTS_BASED))
{
KeAcquireSpinLockAtDpcLevel(&QueryInfo->DpcLock);
KeInsertQueueDpc(&QueryInfo->DeliveryDpc,
QueryInfo,
NULL);
KeReleaseSpinLockFromDpcLevel(&QueryInfo->DpcLock);
}
}
}
return(FALSE);
}
void WmipProcessPrevMcaLogs(
void
)
/*++
Routine Description:
This routine will flush out any of the previous MCA logs and then
hang onto them for WMI to report.
Arguments:
Return Value:
--*/
{
NTSTATUS status;
PERROR_LOGRECORD log;
PMSMCAInfo_RawMCAEvent event;
ULONG size;
PWNODE_SINGLE_INSTANCE wnode;
LIST_ENTRY list;
ULONG prevLogCount;
PMSMCAInfo_Entry record;
ULONG sizeNeeded;
PAGED_CODE();
InitializeListHead(&list);
sizeNeeded = sizeof(ULONG); // Need space for count of records
prevLogCount = 0;
do
{
//
// Read a MCA log out of the HAL
//
status = WmipGetLogFromHal(HalMcaLogInformation,
WmipMcaQueryInfo.Token,
&wnode,
&log,
&size,
WmipMcaQueryInfo.MaxSize,
&WmipMcaQueryInfo.WnodeGuid);
if (NT_SUCCESS(status))
{
//
// Previous logs have a ErrorSeverity of Fatal since they
// were fatal and brought down the system in last boot.
// keep track of how much memory we will need
//
prevLogCount++;
// Need space for record length and
// record padded to DWORD
sizeNeeded += sizeof(ULONG) + ((size +3)&~3);
InsertTailList(&list, (PLIST_ENTRY)wnode);
WmipGenerateMCAEventlog((PUCHAR)log,
size,
TRUE);
}
} while (NT_SUCCESS(status));
if (! IsListEmpty(&list))
{
//
// We have collected a set of previous logs, so we need to
// build the buffer containing the aggregation of those logs.
// The buffer will correspond to the entire MOF structure for
// the MSMCAInfo_RawMCAData class
//
WmipRawMCA = (PMSMCAInfo_RawMCAData)ExAllocatePoolWithTag(PagedPool,
sizeNeeded,
WmipMCAPoolTag);
//
// Fill in the count of logs that follow
//
if (WmipRawMCA != NULL)
{
WmipRawMCA->Count = prevLogCount;
}
//
// Loop over all previous logs
//
WmipRawMCASize = sizeNeeded;
record = &WmipRawMCA->Records[0];
while (! IsListEmpty(&list))
{
wnode = (PWNODE_SINGLE_INSTANCE)RemoveHeadList(&list);
if (WmipRawMCA != NULL)
{
//
// Get the log back from within the wnode
//
event = (PMSMCAInfo_RawMCAEvent)OffsetToPtr(wnode, wnode->DataBlockOffset);
//
// Copy the log data into our buffer. Note that we
// assume there will only be 1 record within the event
//
size = event->Records[0].Length;
record->Length = size;
RtlCopyMemory(&record->Data[0], &event->Records[0].Data[0], size);
size = FIELD_OFFSET(MSMCAInfo_Entry, Data) + (size +3)&~3;
record = (PMSMCAInfo_Entry)((PUCHAR)record + size);
}
ExFreePool(wnode);
}
}
}
//#define TEST_EARLY_CPE
#ifdef TEST_EARLY_CPE
void WmipTestEarlyCPE(
void
)
{
//
// Test code to generate a previous MCA without having
// had generate one previously
//
PERROR_SMBIOS s;
UCHAR Buffer[0x400];
PERROR_RECORD_HEADER rh;
PERROR_SECTION_HEADER sh;
#define ERROR_SMBIOS_GUID \
{ 0xe429faf5, 0x3cb7, 0x11d4, { 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 }}
ERROR_DEVICE_GUID ErrorSmbiosGuid = ERROR_SMBIOS_GUID;
rh = (PERROR_RECORD_HEADER)Buffer;
rh->Id = 0x12345678;
rh->Revision.Revision = 0x0200;
rh->Valid.Valid = 0;
rh->TimeStamp.TimeStamp = 0x2001031900165323;
sh = (PERROR_SECTION_HEADER)((PUCHAR)rh + sizeof(ERROR_RECORD_HEADER));
memset(sh, 0, sizeof(Buffer));
sh->Revision.Revision = 0x0200;
sh->RecoveryInfo.RecoveryInfo = 0;
sh->Length = sizeof(ERROR_SMBIOS);
sh->Guid = ErrorSmbiosGuid;
s = (PERROR_SMBIOS)sh;
s->Valid.Valid = 0;
s->Valid.EventType = 1;
s->EventType = 0xa0;
rh->Length = sizeof(ERROR_RECORD_HEADER) + sh->Length;
HalSetSystemInformation(HalCpeLog,
rh->Length,
rh);
}
#endif
void WmipInsertQueueMCEDpc(
PMCEQUERYINFO QueryInfo
)
{
KIRQL OldIrql;
KeAcquireSpinLock(&QueryInfo->DpcLock,
&OldIrql);
KeInsertQueueDpc(&QueryInfo->DeliveryDpc,
QueryInfo,
NULL);
KeReleaseSpinLock(&QueryInfo->DpcLock,
OldIrql);
}
NTSTATUS WmipRegisterMcaHandler(
ULONG Phase
)
/*++
Routine Description:
This routine will register a kernel MCA and CMC handler with the
hal
Arguments:
Return Value:
NT status code
--*/
{
KERNEL_ERROR_HANDLER_INFO KernelMcaHandlerInfo;
NTSTATUS Status;
HAL_ERROR_INFO HalErrorInfo;
ULONG ReturnSize;
LARGE_INTEGER li;
PAGED_CODE();
if (Phase == 0)
{
//
// Phase 0 initialization is done before device drivers are
// loaded so that the kernel can register its kernel error
// handler before any driver gets a chance to do so.
//
//
// Validate registry values
//
if (WmipCorrectedEventlogCounter == 0)
{
//
// set corrected eventlog counter to -1 to indicate that no
// eventlog suppression should occur
//
WmipCorrectedEventlogCounter = 0xffffffff;
}
//
// Get the size of the logs and any polling/interrupt policies
//
HalErrorInfo.Version = HAL_ERROR_INFO_VERSION;
Status = HalQuerySystemInformation(HalErrorInformation,
sizeof(HAL_ERROR_INFO),
&HalErrorInfo,
&ReturnSize);
if ((NT_SUCCESS(Status)) &&
(ReturnSize >= sizeof(HAL_ERROR_INFO)))
{
//
// Initialize MCA QueryInfo structure
//
if (HalErrorInfo.McaMaxSize != 0)
{
WmipMcaQueryInfo.MaxSize = HalErrorInfo.McaMaxSize;
}
WmipMcaQueryInfo.Token = (PVOID)(ULONG_PTR) HalErrorInfo.McaKernelToken;
//
// Initialize DPC and Workitem for processing
//
KeInitializeDpc(&WmipMcaQueryInfo.DeliveryDpc,
WmipMceDpcRoutine,
NULL);
KeInitializeDpc(&WmipMcaQueryInfo.PollingDpc,
WmipPollingDpcRoutine,
&WmipMcaQueryInfo);
ExInitializeWorkItem(&WmipMcaQueryInfo.WorkItem,
WmipMceWorkerRoutine,
&WmipMcaQueryInfo);
//
// Initialize CMC QueryInfo structure
//
if (HalErrorInfo.CmcMaxSize != 0)
{
WmipCmcQueryInfo.MaxSize = HalErrorInfo.CmcMaxSize;
}
WmipCmcQueryInfo.PollFrequency = HalErrorInfo.CmcPollingInterval;
WmipCmcQueryInfo.Token = (PVOID)(ULONG_PTR) HalErrorInfo.CmcKernelToken;
//
// Initialize DPC and Workitem for processing
//
KeInitializeSpinLock(&WmipCmcQueryInfo.DpcLock);
KeInitializeDpc(&WmipCmcQueryInfo.DeliveryDpc,
WmipMceDpcRoutine,
NULL);
KeInitializeDpc(&WmipCmcQueryInfo.PollingDpc,
WmipPollingDpcRoutine,
&WmipCmcQueryInfo);
ExInitializeWorkItem(&WmipCmcQueryInfo.WorkItem,
WmipMceWorkerRoutine,
&WmipCmcQueryInfo);
KeInitializeTimerEx(&WmipCmcQueryInfo.PollingTimer,
NotificationTimer);
//
// Initialize CPE QueryInfo structure
//
if (HalErrorInfo.CpeMaxSize != 0)
{
WmipCpeQueryInfo.MaxSize = HalErrorInfo.CpeMaxSize;
}
WmipCpeQueryInfo.PollFrequency = HalErrorInfo.CpePollingInterval;
WmipCpeQueryInfo.Token = (PVOID)(ULONG_PTR) HalErrorInfo.CpeKernelToken;
//
// Initialize DPC and Workitem for processing
//
KeInitializeSpinLock(&WmipCpeQueryInfo.DpcLock);
KeInitializeDpc(&WmipCpeQueryInfo.DeliveryDpc,
WmipMceDpcRoutine,
NULL);
KeInitializeDpc(&WmipCpeQueryInfo.PollingDpc,
WmipPollingDpcRoutine,
&WmipCpeQueryInfo);
ExInitializeWorkItem(&WmipCpeQueryInfo.WorkItem,
WmipMceWorkerRoutine,
&WmipCpeQueryInfo);
KeInitializeTimerEx(&WmipCpeQueryInfo.PollingTimer,
NotificationTimer);
//
// Register our CMC and MCA callbacks. And if interrupt driven CPE
// callbacks are enabled register them too
//
KernelMcaHandlerInfo.Version = KERNEL_ERROR_HANDLER_VERSION;
KernelMcaHandlerInfo.KernelMcaDelivery = WmipMceDelivery;
KernelMcaHandlerInfo.KernelCmcDelivery = WmipMceDelivery;
KernelMcaHandlerInfo.KernelCpeDelivery = WmipMceDelivery;
KernelMcaHandlerInfo.KernelMceDelivery = WmipMceEventDelivery;
Status = HalSetSystemInformation(HalKernelErrorHandler,
sizeof(KERNEL_ERROR_HANDLER_INFO),
&KernelMcaHandlerInfo);
if (NT_SUCCESS(Status))
{
WmipMCEState = MCE_STATE_REGISTERED;
#ifdef TEST_EARLY_CPE
WmipTestEarlyCPE();
#endif
} else {
WmipMCEState = (ULONG) MCE_STATE_ERROR;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_MCA_LEVEL | DPFLTR_ERROR_LEVEL,
"WMI: Error %x registering MCA error handlers\n",
Status));
}
}
} else if (WmipMCEState != MCE_STATE_ERROR) {
//
// Phase 1 initialization is done after all of the boot drivers
// have loaded and have had a chance to register for WMI event
// notifications. At this point it is safe to go ahead and send
// wmi events for MCA, CMC, CPE, etc
//
// If there were any MCA logs generated prior to boot then get
// them out of the HAL and process them. Do this before
// starting any polling since the SAL likes to have the
// previous MCA records removed before being polled for CPE and
// CMC
//
#if 0
// DEBUG
//
// Test code to generate a previous MCA without having
// had generate one previously
//
{
PERROR_SMBIOS s;
UCHAR Buffer[0x400];
PERROR_RECORD_HEADER rh;
PERROR_SECTION_HEADER sh;
#define ERROR_SMBIOS_GUID \
{ 0xe429faf5, 0x3cb7, 0x11d4, { 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 }}
ERROR_DEVICE_GUID ErrorSmbiosGuid = ERROR_SMBIOS_GUID;
rh = (PERROR_RECORD_HEADER)Buffer;
rh->Id = 0x12345678;
rh->Revision.Revision = 0x0200;
rh->Valid.Valid = 0;
rh->TimeStamp.TimeStamp = 0x2001031900165323;
sh = (PERROR_SECTION_HEADER)((PUCHAR)rh + sizeof(ERROR_RECORD_HEADER));
memset(sh, 0, sizeof(Buffer));
sh->Revision.Revision = 0x0200;
sh->RecoveryInfo.RecoveryInfo = 0;
sh->Length = sizeof(ERROR_SMBIOS);
sh->Guid = ErrorSmbiosGuid;
s = (PERROR_SMBIOS)sh;
s->Valid.Valid = 0;
s->Valid.EventType = 1;
s->EventType = 0xa0;
rh->Length = sizeof(ERROR_RECORD_HEADER) + sh->Length;
WmipGenerateMCAEventlog(Buffer,
rh->Length,
TRUE);
}
// DEBUG
#endif
HalErrorInfo.Version = HAL_ERROR_INFO_VERSION;
Status = HalQuerySystemInformation(HalErrorInformation,
sizeof(HAL_ERROR_INFO),
&HalErrorInfo,
&ReturnSize);
if ((NT_SUCCESS(Status)) &&
(ReturnSize >= sizeof(HAL_ERROR_INFO)))
{
if (HalErrorInfo.McaPreviousEventsCount != 0)
{
//
// We need to flush out any previous MCA logs and then
// make them available via WMI
//
WmipProcessPrevMcaLogs();
}
}
//
// Establish polling timer for CMC, if needed
//
if ((WmipCmcQueryInfo.PollFrequency != HAL_CMC_DISABLED) &&
(WmipCmcQueryInfo.PollFrequency != HAL_CMC_INTERRUPTS_BASED))
{
li.QuadPart = -1 * (WmipCmcQueryInfo.PollFrequency * 1000000000);
KeSetTimerEx(&WmipCmcQueryInfo.PollingTimer,
li,
WmipCmcQueryInfo.PollFrequency * 1000,
&WmipCmcQueryInfo.PollingDpc);
} else if (WmipCmcQueryInfo.PollFrequency == HAL_CMC_INTERRUPTS_BASED) {
//
// CMC is interrupt based so we need to kick off an attempt
// to read any CMC that had previously occured
//
WmipInsertQueueMCEDpc(&WmipCmcQueryInfo);
}
//
// Establish polling timer for Cpe, if needed
//
if ((WmipCpeQueryInfo.PollFrequency != HAL_CPE_DISABLED) &&
(WmipCpeQueryInfo.PollFrequency != HAL_CPE_INTERRUPTS_BASED))
{
li.QuadPart = -1 * (WmipCpeQueryInfo.PollFrequency * 1000000000);
KeSetTimerEx(&WmipCpeQueryInfo.PollingTimer,
li,
WmipCpeQueryInfo.PollFrequency * 1000,
&WmipCpeQueryInfo.PollingDpc);
} else if (WmipCpeQueryInfo.PollFrequency == HAL_CPE_INTERRUPTS_BASED) {
//
// Cpe is interrupt based so we need to kick off an attempt
// to read any Cpe that had previously occured
//
WmipInsertQueueMCEDpc(&WmipCpeQueryInfo);
}
//
// Flag that we are now able to start firing events
//
WmipMCEState = MCE_STATE_RUNNING;
Status = STATUS_SUCCESS;
}
else {
Status = STATUS_UNSUCCESSFUL;
}
return(Status);
}
NTSTATUS WmipGetRawMCAInfo(
OUT PUCHAR Buffer,
IN OUT PULONG BufferSize
)
/*++
Routine Description:
Return raw MCA log that was already retrieved from hal
Arguments:
Return Value:
NT status code
--*/
{
NTSTATUS status;
PAGED_CODE();
if (WmipRawMCA != NULL)
{
//
// THere are logs so copy over all of the logs
//
if (*BufferSize >= WmipRawMCASize)
{
RtlCopyMemory(Buffer, WmipRawMCA, WmipRawMCASize);
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
*BufferSize = WmipRawMCASize;
} else {
//
// There are no logs so return no records
//
if (*BufferSize >= sizeof(ULONG))
{
*(PULONG)Buffer = 0;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
*BufferSize = sizeof(ULONG);
}
return(status);
}
NTSTATUS WmipGetLogFromHal(
IN HAL_QUERY_INFORMATION_CLASS InfoClass,
IN PVOID Token,
IN OUT PWNODE_SINGLE_INSTANCE *Wnode,
OUT PERROR_LOGRECORD *Mca,
OUT PULONG McaSize,
IN ULONG MaxSize,
IN LPGUID Guid
)
/*++
Routine Description:
This routine will call the HAL to get a log and possibly build a
wnode event for it.
Arguments:
InfoClass is the HalInformationClass that specifies the log
information to retrieve
Token is the HAL token for the log type
*Wnode returns a pointer to a WNODE_EVENT_ITEM containing the log
information if Wnode is not NULL
*Mca returns a pointer to the log read from the hal. It may point
into the memory pointed to by *Wnode
*McaSize returns with the size of the log infomration.
MaxSize has the maximum size to allocate for the log data
Guid points to the guid to use if a Wnode is built
Return Value:
NT status code
--*/
{
NTSTATUS Status;
PERROR_LOGRECORD Log;
PWNODE_SINGLE_INSTANCE WnodeSI;
PULONG Ptr;
ULONG Size, LogSize, WnodeSize;
PAGED_CODE();
//
// If we are reading directly into a wnode then set this up
//
if (Wnode != NULL)
{
WnodeSize = FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData) +
2 * sizeof(ULONG);
} else {
WnodeSize = 0;
}
//
// Allocate a buffer to store the log reported from the hal. Note
// that this must be in non paged pool as per the HAL.
//
Size = MaxSize + WnodeSize;
Ptr = ExAllocatePoolWithTag(NonPagedPool,
Size,
WmipMCAPoolTag);
if (Ptr != NULL)
{
Log = (PERROR_LOGRECORD)((PUCHAR)Ptr + WnodeSize);
LogSize = Size - WnodeSize;
*(PVOID *)Log = Token;
Status = HalQuerySystemInformation(InfoClass,
LogSize,
Log,
&LogSize);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
//
// If our buffer was too small then the Hal lied to us when
// it told us the maximum buffer size. This is ok as we'll
// handle this situation by reallocating and trying again
//
ExFreePool(Log);
//
// Reallocate the buffer and call the hal to get the log
//
Size = LogSize + WnodeSize;
Ptr = ExAllocatePoolWithTag(NonPagedPool,
Size,
WmipMCAPoolTag);
if (Ptr != NULL)
{
Log = (PERROR_LOGRECORD)((PUCHAR)Ptr + WnodeSize);
LogSize = Size - WnodeSize;
*(PVOID *)Log = Token;
Status = HalQuerySystemInformation(InfoClass,
LogSize,
Log,
&LogSize);
//
// The hal gave us a buffer size needed that was too
// small, so lets stop right here and let him know]
//
WmipAssert(Status != STATUS_BUFFER_TOO_SMALL);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (NT_SUCCESS(Status))
{
//
// We sucessfully read the data from the hal so build up
// output buffers.
//
if (Wnode != NULL)
{
//
// Caller requested buffer returned within a WNODE, so
// build up the wnode around the log data
//
WnodeSI = (PWNODE_SINGLE_INSTANCE)Ptr;
Status = WmipBuildMcaCmcEvent(WnodeSI,
Guid,
NULL,
LogSize);
*Wnode = WnodeSI;
}
*Mca = Log;
*McaSize = LogSize;
}
if ((! NT_SUCCESS(Status)) && (Ptr != NULL))
{
//
// If the function failed, but we have an allocated buffer
// then clean it up
//
ExFreePool(Ptr);
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return(Status);
}
//
// Unlink and free a buffer to contain the corrected event information.
// Assumes that the SM Critical section is held
//
void WmipFreeCorrectedMCEEvent(
PMCECORRECTEDEVENT Event
)
{
PAGED_CODE();
RemoveEntryList(&Event->List);
WmipCorrectedMCECount--;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_MCA_LEVEL,
"WMI: MCE event %p for type %d freed\n",
Event,
Event->Type));
ExFreePool(Event);
}
PMCECORRECTEDEVENT WmipAllocCorrectedMCEEvent(
MCECORRECTEDTYPE Type
)
/*++
Routine Description:
This routine will allocate and initialize a MCECORRECTEDEVENT
structure for a new corrected mce event that the kernel is
tracking. The routine ensures that only a fixed limit of corrected
MCE events are allocated and if the limit is exceeded, then the
oldest entry is recycled.
This routine assumes that the WmipSMCriticalSection is held
Arguments:
Type is the type of corrected MCE event
Return Value:
pointer to MCECORRECTEDEVENT stucture or NULL if an entry could not
be allocated
--*/
{
PMCECORRECTEDEVENT Event, EventX;
LARGE_INTEGER OldestTime;
PLIST_ENTRY List;
PAGED_CODE();
if (WmipMaxCorrectedMCEOutstanding != 0)
{
if ((WmipCorrectedMCECount < WmipMaxCorrectedMCEOutstanding) ||
(IsListEmpty(&WmipCorrectedMCEHead)))
{
//
// Allocate a new event from pool
//
Event = (PMCECORRECTEDEVENT)ExAllocatePoolWithTag(PagedPool,
sizeof(MCECORRECTEDEVENT),
WmipMCAPoolTag);
if (Event != NULL)
{
WmipCorrectedMCECount++;
}
} else {
//
// There are already enough mce being tracked, so pick the
// oldest and recycle
//
List = WmipCorrectedMCEHead.Flink;
Event = CONTAINING_RECORD(List,
MCECORRECTEDEVENT,
List);
OldestTime = Event->Timestamp;
List = List->Flink;
while (List != &WmipCorrectedMCEHead)
{
EventX = CONTAINING_RECORD(List,
MCECORRECTEDEVENT,
List);
if (EventX->Timestamp.QuadPart < OldestTime.QuadPart)
{
Event = EventX;
OldestTime = EventX->Timestamp;
}
List = List->Flink;
}
RemoveEntryList(&Event->List);
}
} else {
Event = NULL;
}
if (Event != NULL)
{
Event->Type = Type;
Event->Counter = 1;
Event->Flags = 0;
KeQuerySystemTime(&Event->Timestamp);
InsertHeadList(&WmipCorrectedMCEHead,
&Event->List);
}
return(Event);
}
NTSTATUS WmipTrackCorrectedMCE(
IN MCECORRECTEDTYPE Type,
IN PERROR_RECORD_HEADER Record,
#if defined(_IA64_)
IN PERROR_SECTION_HEADER Section,
#endif
OUT ULONG *LogToEventlog
)
{
PLIST_ENTRY List;
PMCECORRECTEDEVENT Event;
LARGE_INTEGER DeltaTime;
PAGED_CODE();
//
// By default we'll always want an eventlog entry for corrected
// errors
//
switch(Type)
{
case CpuCache:
case CpuTlb:
case CpuBus:
case CpuRegFile:
{
LARGE_INTEGER CurrentTime;
ULONG CpuId;
//
// We got a corrected CPU cache error. If this happended on
// this CPU before within a certain time window then we
// want to suppress the eventlog message
//
CpuId = HalpGetFwMceLogProcessorNumber(Record);
KeQuerySystemTime(&CurrentTime);
WmipEnterSMCritSection();
List = WmipCorrectedMCEHead.Flink;
while (List != &WmipCorrectedMCEHead)
{
Event = CONTAINING_RECORD(List,
MCECORRECTEDEVENT,
List);
if ((Type == Event->Type) &&
(CpuId == Event->CpuId))
{
//
// We have seen a cpu error on this cpu before,
// check if it was within the time interval
//
DeltaTime.QuadPart = (CurrentTime.QuadPart -
Event->Timestamp.QuadPart) /
1000;
if ( (ULONG)DeltaTime.QuadPart <= WmipCoalesceCorrectedErrorInterval)
{
//
// Since it is within the interval, we suppress
// the event
//
*LogToEventlog = 0;
} else {
//
// Since it is not within the interval we do
// not suppress the event, but do need to
// update the time that the last error occurred
//
Event->Timestamp = CurrentTime;
}
goto CpuDone;
}
List = List->Flink;
}
//
// This appears to be the first time we've seen
// this physical address. Build an event structure
// for it and put it on the watch list
//
Event = WmipAllocCorrectedMCEEvent(Type);
if (Event != NULL)
{
Event->CpuId = CpuId;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_MCA_LEVEL,
"WMI: MCE event %p for type %d, cpuid %d added\n",
Event,
Event->Type,
Event->CpuId));
}
CpuDone:
WmipLeaveSMCritSection();
break;
}
case SingleBitEcc:
{
#if defined(_IA64_)
PERROR_MEMORY Memory;
LARGE_INTEGER BytesRemoved;
PHYSICAL_ADDRESS Address;
NTSTATUS Status;
//
// We got a single bit ECC error. See if the physical
// address for it is already on the list and if so bump the
// counter and possibly try to remove the physical memory
// form the system. If not then create a new entry for the
// error.
//
Memory = (PERROR_MEMORY)Section;
if (Memory->Valid.PhysicalAddress == 1)
{
//
// Round down the the nearest page boundry since we are
// tracking errors on a page basis. This means that 2
// errors at different addresses in the same page are
// considered 2 instances of the same error
//
Address.QuadPart = (Memory->PhysicalAddress) & ~(PAGE_SIZE-1);
WmipEnterSMCritSection();
List = WmipCorrectedMCEHead.Flink;
while (List != &WmipCorrectedMCEHead)
{
Event = CONTAINING_RECORD(List,
MCECORRECTEDEVENT,
List);
if ((Type == Event->Type) &&
((Event->Flags & CORRECTED_MCE_EVENT_BUSY) == 0) &&
(Address.QuadPart == Event->SingleBitEccAddress.QuadPart))
{
//
// Don't report multiple errors for the same
// page ever, but update to the current
// timestamp
//
*LogToEventlog = 0;
KeQuerySystemTime(&Event->Timestamp);
if ((WmipSingleBitEccErrorThreshold != 0) &&
(++Event->Counter >= WmipSingleBitEccErrorThreshold))
{
//
// We have crossed the threshold so lets
// attempt to map out the memory.
// Mark the entry as busy and release the
// critical section since mapping out the
// memory may take a long time.
//
Event->Flags |= CORRECTED_MCE_EVENT_BUSY;
WmipLeaveSMCritSection();
//
// MmMarkPhysicalMmemoryAsBad
// requires that the address and
// size be page aligned
//
BytesRemoved.QuadPart = PAGE_SIZE;
Status = MmMarkPhysicalMemoryAsBad(&Address,
&BytesRemoved);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: Physical Address %p removal -> %x\n",
Address.QuadPart,
Status));
if (NT_SUCCESS(Status))
{
//
// Fire off a wmi event to announce
// that the memory has been mapped out
//
WmipFireOffWmiEvent(&WmipMSMCAEvent_MemoryPageRemoved,
sizeof(PHYSICAL_ADDRESS),
&Address);
//
// SInce mapping succeeded, we do not
// expect to see the physical address
// again so we can remove it from the
// list of tracked MCE
//
WmipEnterSMCritSection();
WmipFreeCorrectedMCEEvent(Event);
} else {
Event->Flags &= ~CORRECTED_MCE_EVENT_BUSY;
WmipEnterSMCritSection();
}
}
goto MemoryDone;
}
List = List->Flink;
}
//
// This appears to be the first time we've seen
// this physical address. Build an event structure
// for it and put it on the watch list
//
Event = WmipAllocCorrectedMCEEvent(Type);
if (Event != NULL)
{
Event->SingleBitEccAddress = Address;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_MCA_LEVEL,
"WMI: MCE event %p for type %d, physaddr %I64x added\n",
Event,
Event->Type,
Event->SingleBitEccAddress.QuadPart));
}
MemoryDone:
WmipLeaveSMCritSection();
}
#endif
break;
}
default:
{
WmipAssert(FALSE);
}
}
return(STATUS_SUCCESS);
}
typedef enum
{
CpuStateCheckCache = 0,
CpuStateCheckTLB = 1,
CpuStateCheckBus = 2,
CpuStateCheckRegFile = 3,
CpuStateCheckMS = 4
};
void WmipGenerateMCAEventlog(
PUCHAR ErrorLog,
ULONG ErrorLogSize,
BOOLEAN IsFatal
)
{
PERROR_RECORD_HEADER RecordHeader;
#if defined(_IA64_)
PERROR_SECTION_HEADER SectionHeader;
PERROR_MODINFO ModInfo;
#endif
NTSTATUS Status = STATUS_INVALID_PARAMETER;
PWCHAR w;
ULONG BufferSize;
PUCHAR Buffer, RawPtr = NULL;
PWNODE_SINGLE_INSTANCE Wnode;
PMSMCAEvent_Header Header;
PAGED_CODE();
RecordHeader = (PERROR_RECORD_HEADER)ErrorLog;
//
// Allocate a buffer large enough to accomodate any type of MCA.
// Right now the largest is MSMCAEvent_MemoryError. If this changes
// then this code should be updated
//
BufferSize = ((sizeof(WNODE_SINGLE_INSTANCE) +
(sizeof(USHORT) + sizeof(MCA_EVENT_INSTANCE_NAME)) +7) & ~7) +
sizeof(MSMCAEvent_MemoryError) +
ErrorLogSize;
//
// Allocate a buffer to build the event
//
Buffer = ExAllocatePoolWithTag(PagedPool,
BufferSize,
WmipMCAPoolTag);
if (Buffer != NULL)
{
//
// Fill in the common fields of the WNODE
//
Wnode = (PWNODE_SINGLE_INSTANCE)Buffer;
Wnode->WnodeHeader.BufferSize = BufferSize;
Wnode->WnodeHeader.Linkage = 0;
WmiInsertTimestamp(&Wnode->WnodeHeader);
Wnode->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
WNODE_FLAG_EVENT_ITEM;
Wnode->OffsetInstanceName = sizeof(WNODE_SINGLE_INSTANCE);
Wnode->DataBlockOffset = ((sizeof(WNODE_SINGLE_INSTANCE) +
(sizeof(USHORT) + sizeof(MCA_EVENT_INSTANCE_NAME)) +7) & ~7);
w = (PWCHAR)OffsetToPtr(Wnode, Wnode->OffsetInstanceName);
*w++ = sizeof(MCA_EVENT_INSTANCE_NAME);
wcscpy(w, MCA_EVENT_INSTANCE_NAME);
//
// Fill in the common fields of the event data
//
Header = (PMSMCAEvent_Header)OffsetToPtr(Wnode, Wnode->DataBlockOffset);
Header->Cpu = MCA_UNDEFINED_CPU; // assume CPU will be undefined
Header->AdditionalErrors = 0;
Header->LogToEventlog = 1;
#if defined(_IA64_)
if ((ErrorLogSize < sizeof(ERROR_RECORD_HEADER)) ||
(RecordHeader->Revision.Major != ERROR_MAJOR_REVISION_SAL_03_00) ||
(RecordHeader->Length > ErrorLogSize))
{
//
// Record header is not SAL 3.0 compliant so we do not try
// to interpert the record. It is not compliant for one of
// these reasons:
//
// 1. The error record size is not large enough to contain
// the entire error record header.
// 2. The Major revision number does not match the major
// revision number expected by the code. Note that the
// minor revision number is not checked since changes to
// the minor revision number do not affect the format of
// the error record or sections.
// 3. The error record size as specified in the error
// record header does not match the size obtained from
// the firmware.
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: Invalid MCA Record revision %x or size %d at %p\n"
"do !mca %p to dump MCA record\n",
RecordHeader->Revision,
RecordHeader->Length,
RecordHeader,
RecordHeader));
#endif
Status = STATUS_INVALID_PARAMETER;
#if defined(_IA64_)
} else {
ULONG SizeUsed;
ULONG CpuErrorState = CpuStateCheckCache;
ULONG CpuErrorIndex = 0;
BOOLEAN AdvanceSection;
BOOLEAN FirstError;
//
// Valid 3.0 record, gather the record id and severity from
// the header
//
Header->RecordId = RecordHeader->Id;
Header->ErrorSeverity = RecordHeader->ErrorSeverity;
Header->Cpu = HalpGetFwMceLogProcessorNumber(RecordHeader);
//
// Use the error severity value in the record header to
// determine if the error was fatal. If the value is
// ErrorRecoverable then assume that the error was fatal
// since the HAL will change this value to ErrorCorrected
//
IsFatal = (RecordHeader->ErrorSeverity != ErrorCorrected ? TRUE : FALSE);
//
// Loop over all sections within the record.
//
// CONSIDER: Is it possible to have a record that only has a record
// header and no sections
//
SizeUsed = sizeof(ERROR_RECORD_HEADER);
ModInfo = NULL;
FirstError = TRUE;
while (SizeUsed < ErrorLogSize)
{
//
// Advance to the next section in the record
//
SectionHeader = (PERROR_SECTION_HEADER)(ErrorLog + SizeUsed);
AdvanceSection = TRUE;
Header->AdditionalErrors++;
//
// First validate that this is a valid section
//
if (((SizeUsed + sizeof(ERROR_SECTION_HEADER)) > ErrorLogSize) ||
(SectionHeader->Revision.Revision != SAL_30_ERROR_REVISION) ||
((SizeUsed + SectionHeader->Length) > ErrorLogSize))
{
//
// Not valid section header so we'll give up on
// the whole record. This could be because
//
// 1. There is not enough room in the buffer passed
// by the FW for a complete section header
// 2. The section header revision is not correct
// 3. There is not enough room in the buffer passed
// by the FW for the complete section
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: Invalid MCA SectionHeader revision %d or length %d at %p\n"
"do !mca %p to dump MCA record\n",
SectionHeader->Revision,
SectionHeader->Length,
SectionHeader,
RecordHeader));
//
// We'll break out of the loop since we don't know how to
// move on to the next MCA section since we don't
// understand any format previous to 3.0
//
Status = STATUS_INVALID_PARAMETER;
break;
} else {
//
// Now determine what type of section we have got. This is
// determined by looking at the guid in the section header.
// Each section type has a unique guid value
//
if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorProcessorGuid))
{
//
// Build event for CPU eventlog MCA
//
PMSMCAEvent_CPUError Event;
PERROR_PROCESSOR Processor;
SIZE_T TotalSectionSize;
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_CPUError) );
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates processor error\n",
SectionHeader));
//
// Validate that the section length is large
// enough to accomodate all of the information
// that it declares
//
if (SectionHeader->Length >= sizeof(ERROR_PROCESSOR))
{
Event = (PMSMCAEvent_CPUError)Header;
Processor = (PERROR_PROCESSOR)SectionHeader;
//
// Assume we won't be able to determine the
// various additional information from the
// error logs
//
if (FirstError)
{
Event->Type = IsFatal ? MCA_ERROR_CPU :
MCA_WARNING_CPU;
Event->MajorErrorType = (ULONG)0xffffffff;
Event->Level = (ULONG)0xffffffff;
Event->CacheOp = (ULONG)0xffffffff;
Event->CacheMesi = (ULONG)0xffffffff;
Event->TLBOp = (ULONG)0xffffffff;
Event->BusType = (ULONG)0xffffffff;
Event->BusSev = (ULONG)0xffffffff;
Event->RegFileId = (ULONG)0xffffffff;
Event->RegFileOp = (ULONG)0xffffffff;
Event->MSSid = (ULONG)0xffffffff;
Event->MSOp = (ULONG)0xffffffff;
Event->MSArrayId = (ULONG)0xffffffff;
Event->MSIndex = (ULONG)0xffffffff;
}
//
// Validate that section is large enough to
// handle all specified ERROR_MODINFO
// structs
//
TotalSectionSize = sizeof(ERROR_PROCESSOR) +
((Processor->Valid.CacheCheckNum +
Processor->Valid.TlbCheckNum +
Processor->Valid.BusCheckNum +
Processor->Valid.RegFileCheckNum +
Processor->Valid.MsCheckNum) *
sizeof(ERROR_MODINFO));
if (SectionHeader->Length >= TotalSectionSize)
{
//
// Initialize pointer to the current ERROR_MOFINFO
//
if (ModInfo == NULL)
{
ModInfo = (PERROR_MODINFO)((PUCHAR)Processor +
sizeof(ERROR_PROCESSOR));
} else {
ModInfo++;
}
switch (CpuErrorState)
{
case CpuStateCheckCache:
{
ERROR_CACHE_CHECK Check;
if (Processor->Valid.CacheCheckNum > CpuErrorIndex)
{
//
// We have a cache error that we need to
// handle.
// Advance to next error in the section,
// but don't advance the section
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA ModInfo %p indicates cache error index %d\n",
ModInfo,
CpuErrorIndex));
if (! IsFatal)
{
WmipTrackCorrectedMCE(CpuCache,
RecordHeader,
SectionHeader,
&Header->LogToEventlog);
}
CpuErrorIndex++;
AdvanceSection = FALSE;
if (FirstError)
{
Event->Type = IsFatal ? MCA_ERROR_CACHE :
MCA_WARNING_CACHE;
Event->MajorErrorType = MCACpuCacheError;
if (ModInfo->Valid.CheckInfo == 1)
{
Check.CacheCheck = ModInfo->CheckInfo.CheckInfo;
Event->Level = (ULONG)Check.Level;
Event->CacheOp = (ULONG)Check.Operation;
if (Check.MESIValid == 1)
{
Event->CacheMesi = (ULONG)Check.MESI;
}
}
}
break;
} else {
CpuErrorState = CpuStateCheckTLB;
CpuErrorIndex = 0;
// Fall through and see if there are any
// TLB errors
}
}
case CpuStateCheckTLB:
{
ERROR_TLB_CHECK Check;
if (Processor->Valid.TlbCheckNum > CpuErrorIndex)
{
//
// We have a cache error that we need to
// handle.
// Advance to next error in the section,
// but don't advance the section
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA ModInfo %p indicates TLB error index %d\n",
ModInfo,
CpuErrorIndex));
if (! IsFatal)
{
WmipTrackCorrectedMCE(CpuTlb,
RecordHeader,
SectionHeader,
&Header->LogToEventlog);
}
CpuErrorIndex++;
AdvanceSection = FALSE;
if (FirstError)
{
Event->Type = IsFatal ? MCA_ERROR_TLB :
MCA_WARNING_TLB;
Event->MajorErrorType = MCACpuTlbError;
if (ModInfo->Valid.CheckInfo == 1)
{
Check.TlbCheck = ModInfo->CheckInfo.CheckInfo;
Event->Level = (ULONG)Check.Level;
Event->TLBOp = (ULONG)Check.Operation;
}
}
break;
} else {
CpuErrorState = CpuStateCheckBus;
CpuErrorIndex = 0;
// Fall through and see if there are any
// CPU Bus errors
}
}
case CpuStateCheckBus:
{
ERROR_BUS_CHECK Check;
if (Processor->Valid.BusCheckNum > CpuErrorIndex)
{
//
// We have a cache error that we need to
// handle.
// Advance to next error in the section,
// but don't advance the section
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA ModInfo %p indicates bus error index %d\n",
ModInfo,
CpuErrorIndex));
if (! IsFatal)
{
WmipTrackCorrectedMCE(CpuBus,
RecordHeader,
SectionHeader,
&Header->LogToEventlog);
}
CpuErrorIndex++;
AdvanceSection = FALSE;
if (FirstError)
{
Event->Type = IsFatal ? MCA_ERROR_CPU_BUS :
MCA_WARNING_CPU_BUS;
Event->MajorErrorType = MCACpuBusError;
if (ModInfo->Valid.CheckInfo == 1)
{
Check.BusCheck = ModInfo->CheckInfo.CheckInfo;
Event->BusType = (ULONG)Check.Type;
Event->BusSev = (ULONG)Check.Severity;
}
}
break;
} else {
CpuErrorState = CpuStateCheckRegFile;
CpuErrorIndex = 0;
// Fall through and see if there are any
// REG FILE errors
}
}
case CpuStateCheckRegFile:
{
ERROR_REGFILE_CHECK Check;
if (Processor->Valid.RegFileCheckNum > CpuErrorIndex)
{
//
// We have a cache error that we need to
// handle.
// Advance to next error in the section,
// but don't advance the section
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA ModInfo %p indicates reg file error index %d\n",
ModInfo,
CpuErrorIndex));
if (! IsFatal)
{
WmipTrackCorrectedMCE(CpuRegFile,
RecordHeader,
SectionHeader,
&Header->LogToEventlog);
}
CpuErrorIndex++;
AdvanceSection = FALSE;
if (FirstError)
{
Event->Type = IsFatal ? MCA_ERROR_REGISTER_FILE :
MCA_WARNING_REGISTER_FILE;
Event->MajorErrorType = MCACpuRegFileError;
if (ModInfo->Valid.CheckInfo == 1)
{
Check.RegFileCheck = ModInfo->CheckInfo.CheckInfo;
Event->RegFileOp = (ULONG)Check.Operation;
Event->RegFileId = (ULONG)Check.Identifier;
}
}
break;
} else {
CpuErrorState = CpuStateCheckMS;
CpuErrorIndex = 0;
// Fall through and see if there are any
// Micro Architecture errors
}
}
case CpuStateCheckMS:
{
ERROR_MS_CHECK Check;
if (Processor->Valid.MsCheckNum > CpuErrorIndex)
{
//
// We have a cache error that we need to
// handle.
// Advance to next error in the section,
// but don't advance the section
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA ModInfo %p indicates MAS error index %d\n",
ModInfo,
CpuErrorIndex));
CpuErrorIndex++;
AdvanceSection = FALSE;
if (FirstError)
{
Event->Type = IsFatal ? MCA_ERROR_MAS :
MCA_WARNING_MAS;
Event->MajorErrorType = MCACpuMSError;
if (ModInfo->Valid.CheckInfo == 1)
{
Check.MsCheck = ModInfo->CheckInfo.CheckInfo;
Event->MSOp = (ULONG)Check.Operation;
Event->MSSid = (ULONG)Check.StructureIdentifier;
Event->Level = (ULONG)Check.Level;
Event->MSArrayId = (ULONG)Check.ArrayId;
if (Check.IndexValid == 1)
{
Event->MSIndex = (ULONG)Check.Index;
}
}
}
break;
} else {
if (! FirstError)
{
//
// There are no more errors left in the
// error section so we don't want to
// generate anything.
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA ModInfo %p indicates no error index %d\n",
ModInfo,
CpuErrorIndex));
Header->AdditionalErrors--;
goto DontGenerate;
}
}
}
}
if (FirstError)
{
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_CPUErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_CPUError,
RawRecord) +
ErrorLogSize;
}
Status = STATUS_SUCCESS;
}
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Processor Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
} else if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorMemoryGuid)) {
//
// Build event for MEMORY error eventlog MCA
//
PMSMCAEvent_MemoryError Event;
PERROR_MEMORY Memory;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates memory error\n",
SectionHeader));
Status = STATUS_SUCCESS;
if (FirstError)
{
//
// Ensure the record contains all of the
// fields that it is supposed to
//
if (SectionHeader->Length >= sizeof(ERROR_MEMORY))
{
Event = (PMSMCAEvent_MemoryError)Header;
Memory = (PERROR_MEMORY)SectionHeader;
//
// Take note of any recoverable single
// bit ECC errors. This may even cause
// the memory to be mapped out
//
if (! IsFatal)
{
WmipTrackCorrectedMCE(SingleBitEcc,
RecordHeader,
SectionHeader,
&Header->LogToEventlog);
}
//
// Fill in the data from the MCA within the WMI event
//
if ((Memory->Valid.PhysicalAddress == 1) &&
(Memory->Valid.AddressMask == 1) &&
(Memory->Valid.Card == 1) &&
(Memory->Valid.Module == 1))
{
Event->Type = IsFatal ? MCA_ERROR_MEM_1_2_5_4 :
MCA_WARNING_MEM_1_2_5_4;
} else if ((Memory->Valid.PhysicalAddress == 1) &&
(Memory->Valid.AddressMask == 1) &&
(Memory->Valid.Module == 1))
{
Event->Type = IsFatal ? MCA_ERROR_MEM_1_2_5 :
MCA_WARNING_MEM_1_2_5;
} else if (Memory->Valid.PhysicalAddress == 1)
{
Event->Type = IsFatal ? MCA_ERROR_MEM_1_2:
MCA_WARNING_MEM_1_2;
} else {
Event->Type = IsFatal ? MCA_ERROR_MEM_UNKNOWN:
MCA_WARNING_MEM_UNKNOWN;
}
Event->VALIDATION_BITS = Memory->Valid.Valid;
Event->MEM_ERROR_STATUS = Memory->ErrorStatus.Status;
Event->MEM_PHYSICAL_ADDR = Memory->PhysicalAddress;
Event->MEM_PHYSICAL_MASK = Memory->PhysicalAddressMask;
Event->RESPONDER_ID = Memory->ResponderId;
Event->TARGET_ID = Memory->TargetId;
Event->REQUESTOR_ID = Memory->RequestorId;
Event->BUS_SPECIFIC_DATA = Memory->BusSpecificData;
Event->MEM_NODE = Memory->Node;
Event->MEM_CARD = Memory->Card;
Event->MEM_BANK = Memory->Bank;
Event->xMEM_DEVICE = Memory->Device;
Event->MEM_MODULE = Memory->Module;
Event->MEM_ROW = Memory->Row;
Event->MEM_COLUMN = Memory->Column;
Event->MEM_BIT_POSITION = Memory->BitPosition;
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_MemoryErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_MemoryError,
RawRecord) +
ErrorLogSize;
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Memory Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
}
} else if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorPCIBusGuid)) {
//
// Build event for PCI Component MCA
//
PMSMCAEvent_PCIBusError Event;
PERROR_PCI_BUS PciBus;
NTSTATUS PCIBusErrorTypes[] = {
MCA_WARNING_PCI_BUS_PARITY,
MCA_ERROR_PCI_BUS_PARITY,
MCA_WARNING_PCI_BUS_SERR,
MCA_ERROR_PCI_BUS_SERR,
MCA_WARNING_PCI_BUS_MASTER_ABORT,
MCA_ERROR_PCI_BUS_MASTER_ABORT,
MCA_WARNING_PCI_BUS_TIMEOUT,
MCA_ERROR_PCI_BUS_TIMEOUT,
MCA_WARNING_PCI_BUS_PARITY,
MCA_ERROR_PCI_BUS_PARITY,
MCA_WARNING_PCI_BUS_PARITY,
MCA_ERROR_PCI_BUS_PARITY,
MCA_WARNING_PCI_BUS_PARITY,
MCA_ERROR_PCI_BUS_PARITY
};
NTSTATUS PCIBusErrorTypesNoInfo[] = {
MCA_WARNING_PCI_BUS_PARITY_NO_INFO,
MCA_ERROR_PCI_BUS_PARITY_NO_INFO,
MCA_WARNING_PCI_BUS_SERR_NO_INFO,
MCA_ERROR_PCI_BUS_SERR_NO_INFO,
MCA_WARNING_PCI_BUS_MASTER_ABORT_NO_INFO,
MCA_ERROR_PCI_BUS_MASTER_ABORT_NO_INFO,
MCA_WARNING_PCI_BUS_TIMEOUT_NO_INFO,
MCA_ERROR_PCI_BUS_TIMEOUT_NO_INFO,
MCA_WARNING_PCI_BUS_PARITY_NO_INFO,
MCA_ERROR_PCI_BUS_PARITY_NO_INFO,
MCA_WARNING_PCI_BUS_PARITY_NO_INFO,
MCA_ERROR_PCI_BUS_PARITY_NO_INFO,
MCA_WARNING_PCI_BUS_PARITY_NO_INFO,
MCA_ERROR_PCI_BUS_PARITY_NO_INFO
};
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_PCIBusError) );
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates PCI Bus error\n",
SectionHeader));
Status = STATUS_SUCCESS;
if (FirstError)
{
if (SectionHeader->Length >= sizeof(ERROR_PCI_BUS))
{
Event = (PMSMCAEvent_PCIBusError)Header;
PciBus = (PERROR_PCI_BUS)SectionHeader;
//
// Fill in the data from the MCA within the WMI event
//
if ((PciBus->Type.Type >= PciBusDataParityError) &&
(PciBus->Type.Type <= PciCommandParityError))
{
if ((PciBus->Valid.CmdType == 1) &&
(PciBus->Valid.Address == 1) &&
(PciBus->Valid.Id == 1))
{
Event->Type = PCIBusErrorTypes[(2 * (PciBus->Type.Type-1)) +
(IsFatal ? 1 : 0)];
} else {
Event->Type = PCIBusErrorTypesNoInfo[(2 * (PciBus->Type.Type-1)) +
(IsFatal ? 1 : 0)];
}
} else {
Event->Type = IsFatal ? MCA_ERROR_PCI_BUS_UNKNOWN :
MCA_WARNING_PCI_BUS_UNKNOWN;
}
Event->VALIDATION_BITS = PciBus->Valid.Valid;
Event->PCI_BUS_ERROR_STATUS = PciBus->ErrorStatus.Status;
Event->PCI_BUS_ADDRESS = PciBus->Address;
Event->PCI_BUS_DATA = PciBus->Data;
Event->PCI_BUS_CMD = PciBus->CmdType;
Event->PCI_BUS_REQUESTOR_ID = PciBus->RequestorId;
Event->PCI_BUS_RESPONDER_ID = PciBus->ResponderId;
Event->PCI_BUS_TARGET_ID = PciBus->TargetId;
Event->PCI_BUS_ERROR_TYPE = PciBus->Type.Type;
Event->PCI_BUS_ID_BusNumber = PciBus->Id.BusNumber;
Event->PCI_BUS_ID_SegmentNumber = PciBus->Id.SegmentNumber;
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_PCIBusErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_PCIBusError,
RawRecord) +
ErrorLogSize;
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: PCI Bus Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
}
} else if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorPCIComponentGuid)) {
//
// Build event for PCI Component MCA
//
PMSMCAEvent_PCIComponentError Event;
PERROR_PCI_COMPONENT PciComp;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates PCI Component error\n",
SectionHeader));
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_PCIComponentError) );
Status = STATUS_SUCCESS;
if (FirstError)
{
if (SectionHeader->Length >= sizeof(ERROR_PCI_COMPONENT))
{
Event = (PMSMCAEvent_PCIComponentError)Header;
PciComp = (PERROR_PCI_COMPONENT)SectionHeader;
//
// Fill in the data from the MCA within the WMI event
//
Event->Type = IsFatal ? MCA_ERROR_PCI_DEVICE :
MCA_WARNING_PCI_DEVICE;
Event->VALIDATION_BITS = PciComp->Valid.Valid;
Event->PCI_COMP_ERROR_STATUS = PciComp->ErrorStatus.Status;
Event->PCI_COMP_INFO_VendorId = (USHORT)PciComp->Info.VendorId;
Event->PCI_COMP_INFO_DeviceId = (USHORT)PciComp->Info.DeviceId;
Event->PCI_COMP_INFO_ClassCodeInterface = PciComp->Info.ClassCodeInterface;
Event->PCI_COMP_INFO_ClassCodeSubClass = PciComp->Info.ClassCodeSubClass;
Event->PCI_COMP_INFO_ClassCodeBaseClass = PciComp->Info.ClassCodeBaseClass;
Event->PCI_COMP_INFO_FunctionNumber = (UCHAR)PciComp->Info.FunctionNumber;
Event->PCI_COMP_INFO_DeviceNumber = (UCHAR)PciComp->Info.DeviceNumber;
Event->PCI_COMP_INFO_BusNumber = (UCHAR)PciComp->Info.BusNumber;
Event->PCI_COMP_INFO_SegmentNumber = (UCHAR)PciComp->Info.SegmentNumber;
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_PCIComponentErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_PCIComponentError,
RawRecord) +
ErrorLogSize;
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: PCI Component Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
}
} else if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorSELGuid)) {
//
// Build event for System Eventlog MCA
//
PMSMCAEvent_SystemEventError Event;
PERROR_SYSTEM_EVENT_LOG Sel;
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_SystemEventError) );
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates SEL error\n",
SectionHeader));
Status = STATUS_SUCCESS;
if (FirstError)
{
if (SectionHeader->Length >= sizeof(ERROR_SYSTEM_EVENT_LOG))
{
Event = (PMSMCAEvent_SystemEventError)Header;
Sel = (PERROR_SYSTEM_EVENT_LOG)SectionHeader;
//
// Fill in the data from the MCA within the WMI event
//
Event->Type = IsFatal ? MCA_ERROR_SYSTEM_EVENT :
MCA_WARNING_SYSTEM_EVENT;
Event->VALIDATION_BITS = Sel->Valid.Valid;
Event->SEL_RECORD_ID = Sel->RecordId;
Event->SEL_RECORD_TYPE = Sel->RecordType;
Event->SEL_TIME_STAMP = Sel->TimeStamp;
Event->SEL_GENERATOR_ID = Sel->GeneratorId;
Event->SEL_EVM_REV = Sel->EVMRevision;
Event->SEL_SENSOR_TYPE = Sel->SensorType;
Event->SEL_SENSOR_NUM = Sel->SensorNumber;
Event->SEL_EVENT_DIR_TYPE = Sel->EventDir;
Event->SEL_DATA1 = Sel->Data1;
Event->SEL_DATA2 = Sel->Data2;
Event->SEL_DATA3 = Sel->Data3;
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_SystemEventErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_SystemEventError,
RawRecord) +
ErrorLogSize;
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: System Eventlog Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
}
} else if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorSMBIOSGuid)) {
//
// Build event for SMBIOS MCA
//
PMSMCAEvent_SMBIOSError Event;
PERROR_SMBIOS Smbios;
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_SMBIOSError) );
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates smbios error\n",
SectionHeader));
Status = STATUS_SUCCESS;
if (FirstError)
{
if (SectionHeader->Length >= sizeof(ERROR_SMBIOS))
{
Event = (PMSMCAEvent_SMBIOSError)Header;
Smbios = (PERROR_SMBIOS)SectionHeader;
//
// Fill in the data from the MCA within the WMI event
//
Event->Type = IsFatal ? MCA_ERROR_SMBIOS :
MCA_WARNING_SMBIOS;
Event->VALIDATION_BITS = Smbios->Valid.Valid;
Event->SMBIOS_EVENT_TYPE = Smbios->EventType;
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_SMBIOSErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_SMBIOSError,
RawRecord) +
ErrorLogSize;
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: SMBIOS Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
}
} else if (IsEqualGUID(&SectionHeader->Guid, &WmipErrorSpecificGuid)) {
//
// Build event for Platform Specific MCA
//
PMSMCAEvent_PlatformSpecificError Event;
PERROR_PLATFORM_SPECIFIC Specific;
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_PlatformSpecificError) );
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: MCA Section %p indicates platform specific error\n",
SectionHeader));
Status = STATUS_SUCCESS;
if (FirstError)
{
if (SectionHeader->Length >= sizeof(ERROR_PLATFORM_SPECIFIC))
{
Event = (PMSMCAEvent_PlatformSpecificError)Header;
Specific = (PERROR_PLATFORM_SPECIFIC)SectionHeader;
//
// Fill in the data from the MCA within the WMI event
//
Event->Type = IsFatal ? MCA_ERROR_PLATFORM_SPECIFIC :
MCA_WARNING_PLATFORM_SPECIFIC;
Event->VALIDATION_BITS = Specific->Valid.Valid;
Event->PLATFORM_ERROR_STATUS = Specific->ErrorStatus.Status;
#if 0
// TODO: Wait until we figure this out
Event->PLATFORM_REQUESTOR_ID = Specific->;
Event->PLATFORM_RESPONDER_ID = Specific->;
Event->PLATFORM_TARGET_ID = Specific->;
Event->PLATFORM_BUS_SPECIFIC_DATA = Specific->;
Event->OEM_COMPONENT_ID = Specific->[16];
#endif
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_PlatformSpecificErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_PlatformSpecificError,
RawRecord) +
ErrorLogSize;
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: Platform specific Error Section %p has invalid size %d\n",
SectionHeader,
SectionHeader->Length));
Status = STATUS_INVALID_PARAMETER;
break;
}
}
} else {
//
// We don't recognize the guid, so we use a very generic
// eventlog message for it
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: Unknown Error GUID at %p\n",
&SectionHeader->Guid));
//
// If we've already analyzed an error then we
// don't really care that this one can't be
// analyzed
//
if (FirstError)
{
Status = STATUS_INVALID_PARAMETER;
}
}
}
//
// Advance to the next section within the Error record
//
DontGenerate:
if (AdvanceSection)
{
SizeUsed += SectionHeader->Length;
ModInfo = NULL;
}
//
// If we've successfully parsed an error section then
// we want to remember that. Only the first error gets
// analyzed while we calculate the number of additional
// errors following
//
if (NT_SUCCESS(Status))
{
FirstError = FALSE;
}
}
}
#endif
//
// If we were not able to build a specific event type then
// we fallback and fire a generic one
//
if (! NT_SUCCESS(Status))
{
//
// Build event for Unknown MCA
//
PMSMCAEvent_InvalidError Event;
WmipAssert( sizeof(MSMCAEvent_MemoryError) >=
sizeof(MSMCAEvent_InvalidError) );
Event = (PMSMCAEvent_InvalidError)Header;
//
// Fill in the data from the MCA within the WMI event
//
if (Header->Cpu == MCA_UNDEFINED_CPU)
{
Event->Type = IsFatal ? MCA_ERROR_UNKNOWN_NO_CPU :
MCA_WARNING_UNKNOWN_NO_CPU;
} else {
Event->Type = IsFatal ? MCA_ERROR_UNKNOWN :
MCA_WARNING_UNKNOWN;
}
Event->Size = ErrorLogSize;
RawPtr = Event->RawRecord;
//
// Finish filling in WNODE fields
//
Wnode->WnodeHeader.Guid = WmipMSMCAEvent_InvalidErrorGuid;
Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_InvalidError,
RawRecord) +
ErrorLogSize;
}
//
// Adjust the Error event count
//
if (Header->AdditionalErrors > 0)
{
Header->AdditionalErrors--;
}
//
// Put the entire MCA record into the event
//
RtlCopyMemory(RawPtr,
RecordHeader,
ErrorLogSize);
if ((! IsFatal) && (Header->LogToEventlog == 1))
{
if (WmipCorrectedEventlogCounter != 0)
{
//
// Since this is a corrected error that is getting
// logged to the eventlog we need to account for it
//
if ((WmipCorrectedEventlogCounter != 0xffffffff) &&
(--WmipCorrectedEventlogCounter == 0))
{
WmipWriteToEventlog(MCA_INFO_NO_MORE_CORRECTED_ERROR_LOGS,
STATUS_SUCCESS);
}
} else {
//
// We have exceeded the limit of corrected errors that
// we are allowed to write into the eventlog, so we
// just suppress it
//
Header->LogToEventlog = 0;
}
}
//
// Now go and fire off the event
//
if ((WmipDisableMCAPopups == 0) &&
(Header->LogToEventlog != 0))
{
IoRaiseInformationalHardError(STATUS_MCA_OCCURED,
NULL,
NULL);
}
if ((Header->LogToEventlog == 1) ||
(WmipIsWbemRunning()))
{
//
// Only fire off a WMI event if we want to log to the
// eventlog or WBEM is up and running
//
Status = WmipWriteMCAEventLogEvent((PUCHAR)Wnode);
}
if (! NT_SUCCESS(Status))
{
ExFreePool(Wnode);
}
} else {
//
// Not enough memory to do a full MCA event so lets just do a
// generic one
//
WmipWriteToEventlog(IsFatal ? MCA_WARNING_UNKNOWN_NO_CPU :
MCA_ERROR_UNKNOWN_NO_CPU,
STATUS_INSUFFICIENT_RESOURCES);
}
}
NTSTATUS WmipWriteMCAEventLogEvent(
PUCHAR Event
)
{
PWNODE_HEADER Wnode = (PWNODE_HEADER)Event;
NTSTATUS Status;
PAGED_CODE();
WmipEnterSMCritSection();
if (WmipIsWbemRunning() ||
WmipCheckIsWbemRunning())
{
//
// We know WBEM is running so we can just fire off our event
//
WmipLeaveSMCritSection();
Status = IoWMIWriteEvent(Event);
} else {
//
// WBEM is not currently running and so startup a timer that
// will keep polling it
//
if (WmipIsWbemRunningFlag == WBEM_STATUS_UNKNOWN)
{
//
// No one has kicked off the waiting process for wbem so we
// do that here. Note we need to maintain the critical
// section to guard angainst another thread that might be
// trying to startup the waiting process as well. Note that
// if the setup fails we want to stay in the unknown state
// so that the next time an event is fired we can retry
// waiting for wbem
//
Status = WmipSetupWaitForWbem();
if (NT_SUCCESS(Status))
{
WmipIsWbemRunningFlag = WAITING_FOR_WBEM;
}
}
Wnode->ClientContext = Wnode->BufferSize;
InsertTailList(&WmipWaitingMCAEvents,
(PLIST_ENTRY)Event);
WmipLeaveSMCritSection();
Status = STATUS_SUCCESS;
}
return(Status);
}
ULONG WmipWbemMinuteWait = 1;
NTSTATUS WmipSetupWaitForWbem(
void
)
{
LARGE_INTEGER TimeOut;
NTSTATUS Status;
PAGED_CODE();
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: SetupWaitForWbem starting\n"));
//
// Initialize a kernel time to fire periodically so we can
// check if WBEM has started or not
//
KeInitializeTimer(&WmipIsWbemRunningTimer);
KeInitializeDpc(&WmipIsWbemRunningDpc,
WmipIsWbemRunningDispatch,
NULL);
ExInitializeWorkItem(&WmipIsWbemRunningWorkItem,
WmipIsWbemRunningWorker,
NULL);
TimeOut.HighPart = -1;
TimeOut.LowPart = -1 * (WmipWbemMinuteWait * 60 * 1000 * 10000); // 1 minutes
KeSetTimer(&WmipIsWbemRunningTimer,
TimeOut,
&WmipIsWbemRunningDpc);
Status = STATUS_SUCCESS;
return(Status);
}
void WmipIsWbemRunningDispatch(
IN PKDPC Dpc,
IN PVOID DeferredContext, // Not Used
IN PVOID SystemArgument1, // Not Used
IN PVOID SystemArgument2 // Not Used
)
{
UNREFERENCED_PARAMETER (Dpc);
UNREFERENCED_PARAMETER (DeferredContext);
UNREFERENCED_PARAMETER (SystemArgument1);
UNREFERENCED_PARAMETER (SystemArgument2);
ExQueueWorkItem(&WmipIsWbemRunningWorkItem,
DelayedWorkQueue);
}
void WmipIsWbemRunningWorker(
PVOID Context
)
{
LARGE_INTEGER TimeOut;
PAGED_CODE();
UNREFERENCED_PARAMETER (Context);
if (! WmipCheckIsWbemRunning())
{
//
// WBEM is not yet started, so timeout in another minute to
// check again
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: IsWbemRunningWorker starting -> WBEM not started\n"));
TimeOut.HighPart = -1;
TimeOut.LowPart = (ULONG)(-1 * (1 *60 *1000 *10000)); // 1 minutes
KeSetTimer(&WmipIsWbemRunningTimer,
TimeOut,
&WmipIsWbemRunningDpc);
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: WbemRunningWorker found wbem started\n"));
}
}
BOOLEAN WmipCheckIsWbemRunning(
void
)
{
OBJECT_ATTRIBUTES Obj;
UNICODE_STRING Name;
HANDLE Handle;
LARGE_INTEGER TimeOut;
BOOLEAN IsWbemRunning = FALSE;
NTSTATUS Status;
PWNODE_HEADER Wnode;
PAGED_CODE();
RtlInitUnicodeString(&Name,
L"\\BaseNamedObjects\\WBEM_ESS_OPEN_FOR_BUSINESS");
InitializeObjectAttributes(
&Obj,
&Name,
FALSE,
NULL,
NULL
);
Status = ZwOpenEvent(
&Handle,
SYNCHRONIZE,
&Obj
);
if (NT_SUCCESS(Status))
{
TimeOut.QuadPart = 0;
Status = ZwWaitForSingleObject(Handle,
FALSE,
&TimeOut);
if (Status == STATUS_SUCCESS)
{
IsWbemRunning = TRUE;
//
// We've determined that WBEM is running so now lets see if
// another thread has made that dermination as well. If not
// then we can flush the MCA event queue and set the flag
// that WBEM is running
//
WmipEnterSMCritSection();
if (WmipIsWbemRunningFlag != WBEM_IS_RUNNING)
{
//
// Flush the list of all MCA events waiting to be fired
//
while (! IsListEmpty(&WmipWaitingMCAEvents))
{
Wnode = (PWNODE_HEADER)RemoveHeadList(&WmipWaitingMCAEvents);
WmipLeaveSMCritSection();
Wnode->BufferSize = Wnode->ClientContext;
Wnode->Linkage = 0;
Status = IoWMIWriteEvent(Wnode);
if (! NT_SUCCESS(Status))
{
ExFreePool(Wnode);
}
WmipEnterSMCritSection();
}
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_MCA_LEVEL,
"WMI: WBEM is Running and queus flushed\n"));
WmipIsWbemRunningFlag = WBEM_IS_RUNNING;
}
WmipLeaveSMCritSection();
}
ZwClose(Handle);
}
return(IsWbemRunning);
}
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif