void WmipWaitForCollectionEnabled( PBGUIDENTRY GuidEntry ) { PAGED_CODE(); WmipAssert((GuidEntry->Flags & GE_FLAG_COLLECTION_IN_PROGRESS) == GE_FLAG_COLLECTION_IN_PROGRESS); // // Collection Enable/Disable is in progress so // we cannot return just yet. Right now there could be a // disable request being processed and if we didn't wait, we // might get back to this caller before that disable request // got around to realizing that it needs to send and enable // request (needed by this thread's caller). So we'd have a // situation where a thread though that collection was enabled // but in reality it wasn't yet enabled. if ((GuidEntry->Flags & GE_FLAG_WAIT_ENABLED) == 0) { KeInitializeEvent(GuidEntry->CollectInProgress, NotificationEvent, FALSE); GuidEntry->Flags |= GE_FLAG_WAIT_ENABLED; WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p.%p for %p %x created event\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), GuidEntry, GuidEntry->Flags)); } WmipLeaveSMCritSection(); WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p.%p waiting for %p %x on event\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), GuidEntry, GuidEntry->Flags)); KeWaitForSingleObject(GuidEntry->CollectInProgress, Executive, KernelMode, FALSE, NULL); WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p.%p done %p %x waiting on event\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), GuidEntry, GuidEntry->Flags)); WmipEnterSMCritSection(); }
NTSTATUS WmipConstructMCAErrorEvent( IN PMCA_EXCEPTION McaException, IN ULONG ErrorLogSize, IN OUT PWNODE_SINGLE_INSTANCE Wnode, IN OUT PMSMCAEvent_Header Header, IN OUT PUCHAR *RawPtr, IN OUT BOOLEAN *IsFatal ) /*++ Routine Description: This routine attempts to build an MCA error log event from the information in the supplied MCA_EXCEPTION. Arguments: McaException - Pointer to a MCA_EXCEPTION that encapsulates the machine check event. ErrorLogSize - The total size in bytes of the raw error information. Wnode - Pointer to the error event WNODE. Header - Pointer to the common header for all MCA events. RawPtr - Address into which a pointer to the raw MCA data is to be copied. Return Value: STATUS_SUCCESS - If an event is successfully constructed. STATUS_NOT_SUPPORTED - If the routine does not create an event. --*/ { PMSMCAEvent_MemoryHierarchyError memoryEvent; PMSMCAEvent_InvalidError invalidEvent; PMSMCAEvent_TLBError tlbEvent; PMSMCAEvent_BusError busEvent; NTSTATUS status; ULONG mcaCode; PAGED_CODE(); if (*IsFatal) { if (McaException->ExceptionType == HAL_MCA_RECORD) { if (!McaException->u.Mca.Status.MciStats.UnCorrected) { *IsFatal = FALSE; } } } // // Sanity checks on the size of the event structures. None should be // larger than the size of a memory error event. // WmipAssert(sizeof(MSMCAEvent_MemoryError) >= sizeof(MSMCAEvent_InvalidError)); WmipAssert(sizeof(MSMCAEvent_MemoryError) >= sizeof(MSMCAEvent_MemoryHierarchyError)); WmipAssert(sizeof(MSMCAEvent_MemoryError) >= sizeof(MSMCAEvent_TLBError)); WmipAssert(sizeof(MSMCAEvent_MemoryError) >= sizeof(MSMCAEvent_BusError)); // // This routine is only to be called whe the MCA status bits are valid. // WmipAssert(McaException->u.Mca.Status.MciStats.Valid); mcaCode = McaException->u.Mca.Status.MciStats.McaCod; status = STATUS_SUCCESS; if (mcaCode == MCA_CODE_MICROCODE_ROM_PARITY_ERROR || mcaCode == MCA_CODE_EXTERNAL_ERROR || mcaCode == MCA_CODE_FRC_ERROR || mcaCode == MCA_CODE_INTERNAL_TIMER_ERROR) { // // For Microcode ROM parity errors, external errors, FRC errors, and // internal timer errors, we will use the InvalidError event since it // is useful for generating error events with no parameters. // invalidEvent = (PMSMCAEvent_InvalidError)Header; // // Indicate the error type. // if (mcaCode == MCA_CODE_MICROCODE_ROM_PARITY_ERROR) { invalidEvent->Type = (ULONG)MCA_MICROCODE_ROM_PARITY_ERROR; } else if (mcaCode == MCA_CODE_EXTERNAL_ERROR) { invalidEvent->Type = (ULONG)MCA_EXTERNAL_ERROR; } else if (mcaCode == MCA_CODE_FRC_ERROR) { invalidEvent->Type = (ULONG)MCA_FRC_ERROR; } else { invalidEvent->Type = (ULONG)MCA_INTERNALTIMER_ERROR; } // // Fill in the GUID and the size of the data block. // Wnode->WnodeHeader.Guid = WmipMSMCAEvent_InvalidErrGuid; Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_InvalidError, RawRecord) + ErrorLogSize; // // Copy the address of the raw record into the supplied address. // *RawPtr = invalidEvent->RawRecord; // // Indicate the size of the entire record. // invalidEvent->Size = ErrorLogSize; } else if (mcaCode & MCA_CODE_BUS_ERR_MASK) { // // Bus/Interconnect error. Extract the details of the error information // from the preserved MCI_STATS and save it in the bus error event. // busEvent = (PMSMCAEvent_BusError)Header; if (mcaCode & 0x0100) { busEvent->Type = (ULONG)MCA_BUS_TIMEOUT_ERROR; } else { busEvent->Type = (ULONG)MCA_BUS_ERROR; } busEvent->Participation = ((mcaCode & 0x00000600) >> 9); busEvent->MemoryHierarchyLevel = (mcaCode & 0x00000003); busEvent->RequestType = ((mcaCode & 0x000000F0) >> 4); busEvent->MemOrIo = ((mcaCode & 0x0000000C) >> 2); if (McaException->u.Mca.Status.MciStats.AddressValid) { busEvent->Address = McaException->u.Mca.Address.QuadPart; } else { busEvent->Address = (ULONG64)0; } Wnode->WnodeHeader.Guid = WmipMSMCAEvent_BusErrorGuid; Wnode->SizeDataBlock = FIELD_OFFSET(MSMCAEvent_BusError, RawRecord) + ErrorLogSize; busEvent->Size = ErrorLogSize; *RawPtr = busEvent->RawRecord; } else if (mcaCode & MCA_CODE_MEM_HIERARCHY_ERR_MASK) {
ULONG WmipSendDisableRequest( PBGUIDENTRY GuidEntry, BOOLEAN IsEvent, BOOLEAN IsTraceLog, ULONG64 LoggerContext ) /*++ Routine Description: This routine will send an disable collection or notification request to all of the data providers that have registered the guid being disabled. This routine will manage any race conditions that might occur when multiple threads are enabling and disabling the notification simultaneously. This routine is called while the SM critical section is being held and will increment the appropriate reference count. if the ref count transitions from 1 to 0 then the disable request will need to be forwarded to the data providers otherwise the routine is all done and returns. Before sending the disable request the routine checks to see if any enable or disable requests are currently in progress and if not then sets the in progress flag, releases the critical section and sends the disable request. If there was a request in progress then the routine does not send a request, but just returns. When the other thread that was sending the request returns from processing the request it will recheck the refcount and notice that it is 0 and then send the disable request. Arguments: GuidEntry is the Notification entry that describes the guid being enabled. GuidEntry is the guid entry that describes the guid being enabled. For a notification it may be NULL. NotificationContext is the notification context to use if enabling events IsEvent is TRUE if notifications are being enables else FALSE if collection is being enabled IsTraceLog is TRUE if enable is for a trace log guid LoggerContext is a context value to forward in the enable request Return Value: ERROR_SUCCESS or an error code --*/ { ULONG InProgressFlag; ULONG RefCount; ULONG Status; PAGED_CODE(); if (IsEvent) { InProgressFlag = GE_FLAG_NOTIFICATION_IN_PROGRESS; RefCount = GuidEntry->EventRefCount; if (RefCount == 0) { // // A bad data consumer is disabling his event more // than once. Just ignore it return(STATUS_SUCCESS); } RefCount = --GuidEntry->EventRefCount; } else { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p.%p Disabling for %p %x\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), GuidEntry, GuidEntry->Flags)); InProgressFlag = GE_FLAG_COLLECTION_IN_PROGRESS; RefCount = --GuidEntry->CollectRefCount; WmipAssert(RefCount != 0xffffffff); } // // If we have transitioned to a refcount of zero and there is // not a request in progress then forward the disable request. if ((RefCount == 0) && ! (GuidEntry->Flags & InProgressFlag)) { // // Take an extra ref count so that even if this gets // disabled while the disable request is in progress the // GuidEntry will stay valid. GuidEntry->Flags |= InProgressFlag; WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p.%p NE %p flags -> %x at %d\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), GuidEntry, GuidEntry->Flags, __LINE__)); Status = WmipDoDisableRequest(GuidEntry, IsEvent, IsTraceLog, LoggerContext, InProgressFlag); } else { Status = STATUS_SUCCESS; } if (! IsEvent) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p.%p Disable complete for %p %x\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), GuidEntry, GuidEntry->Flags)); } return(Status); }
NTSTATUS WmipSendEnableDisableRequest( UCHAR ActionCode, PBGUIDENTRY GuidEntry, BOOLEAN IsEvent, BOOLEAN IsTraceLog, ULONG64 LoggerContext ) /*++ Routine Description: This routine will deliver an event or collection WNODE to all data providers of a guid. This routine assumes that it is called with the SM critical section held. The routine does not hold the critical section for the duration of the call. Arguments: ActionCode is WMI_ENABLE_EVENTS, WMI_DISABLE_EVENTS, WMI_ENABLE_COLLECTION or WMI_DISABLE_COLLECTION GuidEntry is the guid entry for the guid that is being enabled/disable or collected/stop collected IsEvent is TRUE then ActionCode is to enable or disable events. If FALSE then ActionCode is to enable or disbale collecton IsTraceLog is TRUE then enable is only sent to those guids registered as being a tracelog guid LoggerContext is a logger context handle that should be placed in the HistoricalContext field of the WNODE_HEADER if IsTraceLog is TRUE. Return Value: ERROR_SUCCESS or an error code --*/ { #if DBG #define AVGISPERGUID 1 #else #define AVGISPERGUID 64 #endif PLIST_ENTRY InstanceSetList; PBINSTANCESET InstanceSet; PBDATASOURCE DataSourceArray[AVGISPERGUID]; PBDATASOURCE *DataSourceList; ULONG BufferSize; ULONG Status = 0; PWNODE_HEADER pWnode; ULONG i; PBDATASOURCE DataSource; ULONG DSCount; BOOLEAN IsEnable; ULONG IsFlags, IsUpdate; WMITRACE_NOTIFY_HEADER TraceNotifyHeader; PAGED_CODE(); if (GuidEntry->Flags & GE_FLAG_INTERNAL) { // // Guids that have been unregistered and Internally defined guids // have no data source to send requests to, so just leave happily return(STATUS_SUCCESS); } IsEnable = ((ActionCode == IRP_MN_ENABLE_EVENTS) || (ActionCode == IRP_MN_ENABLE_COLLECTION)); IsFlags = IsEvent ? IS_ENABLE_EVENT : IS_ENABLE_COLLECTION; // // Determine whether this is an update call and reset the bit // IsUpdate = (GuidEntry->Flags & GE_NOTIFICATION_TRACE_UPDATE); // // First we make a list of all of the DataSources that need to be called // while we have the critical section and take a reference on them so // they don't go away after we release them. Note that the DataSource // structure will stay, but the actual data provider may in fact go away. // In this case sending the request will fail. DSCount = 0; if (GuidEntry->ISCount > AVGISPERGUID) { DataSourceList = WmipAlloc(GuidEntry->ISCount * sizeof(PBDATASOURCE)); if (DataSourceList == NULL) { WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: alloc failed for DataSource array in WmipSendEnableDisableRequest\n")); return(STATUS_INSUFFICIENT_RESOURCES); } } else { DataSourceList = &DataSourceArray[0]; } #if DBG memset(DataSourceList, 0, GuidEntry->ISCount * sizeof(PBDATASOURCE)); #endif InstanceSetList = GuidEntry->ISHead.Flink; while ((InstanceSetList != &GuidEntry->ISHead) && (DSCount < GuidEntry->ISCount)) { WmipAssert(DSCount < GuidEntry->ISCount); InstanceSet = CONTAINING_RECORD(InstanceSetList, INSTANCESET, GuidISList); // // We send requests to those data providers that are not inprocs when // it is an event being enabled or it is collection being enabled // and they are defined to be expensive (collection needs to be // enabled) if ( ( (IsTraceLog && (InstanceSet->Flags & IS_TRACED)) || ( ! IsTraceLog && (! (InstanceSet->Flags & IS_TRACED)) && (IsEvent || (InstanceSet->Flags & IS_EXPENSIVE)) ) ) ) { if ( (! IsEnable && (InstanceSet->Flags & IsFlags)) || ((IsEnable && ! (InstanceSet->Flags & IsFlags)) || (IsUpdate && IsTraceLog)) ) { DataSourceList[DSCount] = InstanceSet->DataSource; WmipReferenceDS(DataSourceList[DSCount]); DSCount++; } if (IsEnable) { InstanceSet->Flags |= IsFlags; } else { InstanceSet->Flags &= ~IsFlags; } } InstanceSetList = InstanceSetList->Flink; } if (IsUpdate) { GuidEntry->Flags &= ~GE_NOTIFICATION_TRACE_UPDATE; } WmipLeaveSMCritSection(); // // Now without the critical section we send the request to all of the // data providers. Any new data providers who register after we made our // list will be enabled by the registration code. if (DSCount > 0) { pWnode = &TraceNotifyHeader.Wnode; RtlZeroMemory(pWnode, sizeof(TraceNotifyHeader)); RtlCopyMemory(&pWnode->Guid, &GuidEntry->Guid, sizeof(GUID)); BufferSize = sizeof(WNODE_HEADER); if (IsTraceLog) { BufferSize = sizeof(TraceNotifyHeader); TraceNotifyHeader.LoggerContext = LoggerContext; pWnode->Flags |= WNODE_FLAG_TRACED_GUID; // // If this GUID is already enabled then this must // an update call. So mark it so. // if ( IsEnable && IsUpdate ) { pWnode->ClientContext = IsUpdate; } } pWnode->BufferSize = BufferSize; for (i = 0; i < DSCount; i++) { DataSource = DataSourceList[i]; WmipAssert(DataSource != NULL); if (IsTraceLog) { if (DataSource->Flags & DS_KERNEL_MODE) { pWnode->HistoricalContext = LoggerContext; } else if (DataSource->Flags & DS_USER_MODE) { pWnode->HistoricalContext = 0; } else { ASSERT(FALSE); } } Status |= WmipDeliverWnodeToDS(ActionCode, DataSource, pWnode, BufferSize); WmipUnreferenceDS(DataSource); } } if( ! IsTraceLog ) { Status = STATUS_SUCCESS; } if (DataSourceList != DataSourceArray) { WmipFree(DataSourceList); } WmipEnterSMCritSection(); return(Status); }