1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxTelemetryUm.cpp 8 9 Abstract: 10 11 This module implements a telemetry methods. 12 13 Author: 14 15 16 17 Environment: 18 19 User mode only 20 21 Revision History: 22 23 Notes: 24 25 --*/ 26 27 #include "fxsupportpch.hpp" 28 #include "DriverFrameworks-UserMode-UmEvents.h" 29 #include "FxldrUm.h" 30 #include <winmeta.h> 31 #include <TraceLoggingProvider.h> 32 #include <telemetry\MicrosoftTelemetry.h> 33 #include <rpc.h> 34 #include <rpcndr.h> 35 36 extern "C" { 37 #if defined(EVENT_TRACING) 38 #include "FxTelemetryUm.tmh" 39 #endif 40 } 41 42 /* 8ad60765-a021-4494-8594-9346970cf50f */ 43 TRACELOGGING_DEFINE_PROVIDER(g_TelemetryProvider, 44 UMDF_FX_TRACE_LOGGING_PROVIDER_NAME, 45 (0x8ad60765, 0xa021, 0x4494, 0x85, 0x94, 0x93, 0x46, 0x97, 0x0c, 0xf5, 0x0f), 46 TraceLoggingOptionMicrosoftTelemetry()); 47 48 VOID 49 AllocAndInitializeTelemetryContext( 50 _In_ PFX_TELEMETRY_CONTEXT* TelemetryContext 51 ) 52 { 53 PFX_TELEMETRY_CONTEXT context = NULL; 54 RPC_STATUS status; 55 56 context = (PFX_TELEMETRY_CONTEXT)MxMemory::MxAllocatePoolWithTag(NonPagedPool, 57 sizeof(FX_TELEMETRY_CONTEXT), 58 FX_TAG); 59 if (NULL == context) { 60 goto exit; 61 } 62 63 status = UuidCreate(&(context->DriverSessionGUID)); 64 if ((status != RPC_S_OK) && (status != RPC_S_UUID_LOCAL_ONLY)) { 65 MxMemory::MxFreePool(context); 66 context = NULL; 67 goto exit; 68 } 69 70 context->DoOnceFlagsBitmap = 0; 71 exit: 72 *TelemetryContext = context; 73 } 74 75 VOID 76 RegisterTelemetryProvider( 77 VOID 78 ) 79 { 80 EventRegisterMicrosoft_Windows_DriverFrameworks_UserMode(); 81 82 TraceLoggingRegister(g_TelemetryProvider); 83 } 84 85 VOID 86 UnregisterTelemetryProvider( 87 VOID 88 ) 89 { 90 EventUnregisterMicrosoft_Windows_DriverFrameworks_UserMode(); 91 92 TraceLoggingUnregister(g_TelemetryProvider); 93 } 94 95 VOID 96 LogDeviceStartTelemetryEvent( 97 _In_ PFX_DRIVER_GLOBALS Globals, 98 _In_opt_ FxDevice* Fdo 99 ) 100 { 101 // If provider is not enabled we're done. 102 if (FALSE == FX_TELEMETRY_ENABLED(g_TelemetryProvider, Globals)) { 103 return; 104 } 105 106 // 107 // If we already fired an event during PnP start we are done. This avoids 108 // repeatedly firing events during PnP rebalance. 109 // 110 if (InterlockedBitTestAndSet( 111 &Globals->TelemetryContext->DoOnceFlagsBitmap, 112 DeviceStartEventBit) == 1) { 113 return; 114 } 115 116 // 117 // log the DriverInfo stream. 118 // 119 LogDriverInfoStream(Globals, Fdo); 120 } 121 122 VOID 123 LogDriverInfoStream( 124 _In_ PFX_DRIVER_GLOBALS DriverGlobals, 125 _In_ FxDevice* Fdo 126 ) 127 { 128 LONG error = ERROR_SUCCESS; 129 PWCHAR str = NULL; 130 UFxTelemetryDriverInfo driverInfo = {0}; 131 LPCWSTR hardwareIds = NULL; 132 LPCWSTR setupClass = NULL; 133 LPCWSTR busEnum = NULL; 134 LPCWSTR manufacturer = NULL; 135 UMDF_DRIVER_REGSITRY_INFO devRegInfo = {0}; 136 PCWSTR groupId = NULL; 137 IWudfDeviceStack* devStack = NULL; 138 139 if (Fdo == NULL) { 140 // 141 // Telemetry events are logged during DriverEntry as well to capture non-pnp 142 // and class extension (which are non-pnp drivers) driver info. Although 143 // current UMDF datapoint doesn't have a separate flag for non-pnp driver, 144 // we still want to log the driver name and its properies if available. 145 // 146 devStack = DriverGlobals->Driver->GetDriverObject()->WudfDevStack; 147 if (devStack != NULL) { 148 devStack->GetPdoProperties(&hardwareIds, 149 &setupClass, 150 &busEnum, 151 &manufacturer); 152 } 153 } 154 else { 155 devStack = Fdo->GetDeviceStack(); 156 devStack->GetPdoProperties(&hardwareIds, 157 &setupClass, 158 &busEnum, 159 &manufacturer); 160 161 Fdo->RetrieveDeviceInfoRegistrySettings(&groupId, &devRegInfo); 162 } 163 164 // 165 // Log Driver info 166 // 167 if (Fdo != NULL) { 168 GetDriverInfo(Fdo, &devRegInfo, &driverInfo); 169 } 170 171 UMDF_CENSUS_EVT_WRITE_DEVICE_START(g_TelemetryProvider, 172 DriverGlobals, 173 driverInfo, 174 setupClass, 175 busEnum, 176 hardwareIds, 177 manufacturer); 178 179 if (groupId != NULL) { 180 delete [] groupId; 181 groupId = NULL; 182 } 183 } 184 185 VOID 186 GetDriverInfo( 187 _In_ FxDevice* Fdo, 188 _In_ PUMDF_DRIVER_REGSITRY_INFO RegInfo, 189 _Out_ UFxTelemetryDriverInfo* DriverInfo 190 ) 191 { 192 FxPkgPnp* pnpPkg; 193 USHORT devInfo = 0; 194 WDF_DEVICE_IO_TYPE readWritePreference; 195 WDF_DEVICE_IO_TYPE ioControlPreference; 196 UMDF_DRIVER_REGSITRY_INFO devRegInfo = {0}; 197 PWSTR groupId = NULL; 198 199 pnpPkg = (FxPkgPnp*)Fdo->GetFdoPkg(); 200 devInfo = Fdo->GetDeviceTelemetryInfoFlags(); 201 Fdo->GetDeviceStackIoType(&readWritePreference, &ioControlPreference); 202 203 DriverInfo->bitmap.IsFilter = Fdo->IsFilter(); 204 DriverInfo->bitmap.IsPowerPolicyOwner = pnpPkg->IsPowerPolicyOwner(); 205 DriverInfo->bitmap.IsS0IdleWakeFromS0Enabled = pnpPkg->IsS0IdleWakeFromS0Enabled(); 206 DriverInfo->bitmap.IsS0IdleUsbSSEnabled = pnpPkg->IsS0IdleUsbSSEnabled(); 207 DriverInfo->bitmap.IsS0IdleSystemManaged = pnpPkg->IsS0IdleSystemManaged(); 208 DriverInfo->bitmap.IsSxWakeEnabled = pnpPkg->IsSxWakeEnabled(); 209 DriverInfo->bitmap.IsUsingLevelTriggeredLineInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoLineBasedLevelTriggeredInterrupt); 210 DriverInfo->bitmap.IsUsingEdgeTriggeredLineInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoLineBasedEdgeTriggeredInterrupt); 211 DriverInfo->bitmap.IsUsingMsiXOrSingleMsi22Interrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoMsiXOrSingleMsi22Interrupt); 212 DriverInfo->bitmap.IsUsingMsi22MultiMessageInterrupt = IsDeviceInfoFlagSet(devInfo, DeviceInfoMsi22MultiMessageInterrupt); 213 DriverInfo->bitmap.IsUsingMultipleInterrupt = pnpPkg->HasMultipleInterrupts(); 214 DriverInfo->bitmap.IsDirectHardwareAccessAllowed = Fdo->IsDirectHardwareAccessAllowed(); 215 DriverInfo->bitmap.IsUsingUserModemappingAccessMode = Fdo->AreRegistersMappedToUsermode(); 216 DriverInfo->bitmap.IsKernelModeClientAllowed = RegInfo->IsKernelModeClientAllowed; 217 DriverInfo->bitmap.IsNullFileObjectAllowed = RegInfo->IsNullFileObjectAllowed; 218 DriverInfo->bitmap.IsPoolingDisabled = RegInfo->IsHostProcessSharingDisabled; 219 DriverInfo->bitmap.IsMethodNeitherActionCopy = RegInfo->IsMethodNeitherActionCopy; 220 DriverInfo->bitmap.IsUsingDirectIoForReadWrite = (readWritePreference == WdfDeviceIoDirect); 221 DriverInfo->bitmap.IsUsingDirectIoForIoctl = (ioControlPreference == WdfDeviceIoDirect); 222 DriverInfo->bitmap.IsUsingDriverWppRecorder = Fdo->GetDriver()->IsDriverObjectFlagSet(DriverObjectUmFlagsLoggingEnabled); 223 224 return; 225 } 226 227 _Must_inspect_result_ 228 NTSTATUS 229 GetImageName( 230 _In_ PFX_DRIVER_GLOBALS FxDriverGlobals, 231 _Out_ PUNICODE_STRING ImageName 232 ) 233 /*++ 234 235 Routine Description: 236 Retrieve the ImageName value from the named Service registry key. 237 238 Caller is responsible for freeing the buffer allocated in ImageName::Buffer. 239 240 Arguments: 241 DriverGlobals - pointer to FX_DRIVER_GLOBALS 242 243 ImageeName - Pointer to a UNICODE_STRING which will receive the image name 244 upon a return value of NT_SUCCESS() 245 246 Return Value: 247 NTSTATUS 248 249 --*/ 250 { 251 NTSTATUS status; 252 FxAutoRegKey hKey; 253 DECLARE_CONST_UNICODE_STRING(valueName, L"ImagePath"); 254 UNICODE_STRING imagePath = {0}; 255 UNICODE_STRING imageName = {0}; 256 PKEY_VALUE_PARTIAL_INFORMATION value = NULL; 257 USHORT size; 258 ULONG length, type; 259 PVOID dataBuffer; 260 261 type = REG_SZ; 262 length = 0; 263 264 ASSERT(ImageName != NULL); 265 RtlZeroMemory(ImageName, sizeof(UNICODE_STRING)); 266 267 // 268 // Open driver's Service base key 269 // 270 status = FxRegKey::_OpenKey(NULL, 271 FxDriverGlobals->Driver->GetRegistryPathUnicodeString(), 272 &hKey.m_Key, 273 KEY_READ); 274 if (!NT_SUCCESS(status)) { 275 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 276 "Unable to open driver's service key, %!STATUS!", status); 277 return status; 278 } 279 280 // 281 // Find out how big a buffer we need to allocate if the value is present 282 // 283 status = FxRegKey::_QueryValue(FxDriverGlobals, 284 hKey.m_Key, 285 &valueName, 286 length, 287 NULL, 288 &length, 289 &type); 290 291 // 292 // We expect the list to be bigger then a standard partial, so if it is 293 // not, just bail now. 294 // 295 if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { 296 return STATUS_UNSUCCESSFUL; 297 } 298 299 // 300 // Pool can be paged b/c we are running at PASSIVE_LEVEL and we are going 301 // to free it at the end of this function. 302 // 303 dataBuffer = FxPoolAllocate(FxDriverGlobals, PagedPool, length); 304 if (dataBuffer == NULL) { 305 status = STATUS_INSUFFICIENT_RESOURCES; 306 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 307 "Failed to allocate memory for image path string, %!STATUS!", 308 status); 309 return status; 310 } 311 312 // 313 // Requery now that we have a big enough buffer 314 // 315 status = FxRegKey::_QueryValue(FxDriverGlobals, 316 hKey.m_Key, 317 &valueName, 318 length, 319 dataBuffer, 320 &length, 321 &type); 322 if (!NT_SUCCESS(status)) { 323 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 324 "Failed to get Image name from service key, %!STATUS!", 325 status); 326 goto cleanUp; 327 } 328 329 // 330 // Verify that the data from the registry is a valid string. 331 // 332 if (type != REG_SZ && type != REG_EXPAND_SZ) { 333 return STATUS_OBJECT_TYPE_MISMATCH; 334 } 335 336 if (length == 0 || length > USHORT_MAX) { 337 return STATUS_INVALID_PARAMETER; 338 } 339 340 // 341 // string must be NULL-terminated 342 // 343 PWCHAR str = (PWCHAR) dataBuffer; 344 if (str[(length/sizeof(WCHAR)) - 1] != UNICODE_NULL) { 345 status = STATUS_INVALID_PARAMETER; 346 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 347 "ERROR: string not terminated with NULL, %!status!\n", 348 status); 349 goto cleanUp; 350 } 351 352 RtlInitUnicodeString(&imagePath, (PCWSTR) dataBuffer); 353 354 // 355 // Now read the "ImagePath" and extract just the driver filename as a new 356 // unicode string. 357 // 358 GetNameFromPath(&imagePath, &imageName); 359 360 if (imageName.Length == 0x0) { 361 status = STATUS_INVALID_PARAMETER; 362 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 363 "ERROR: GetNameFromPath could not find a name, %!status!\n", 364 status); 365 goto cleanUp; 366 } 367 368 // 369 // Check for interger overflow for length before we allocate memory 370 // size = path->Length + sizeof(UNICODE_NULL); 371 // len is used below to compute the string size including the NULL, so 372 // compute len to include the terminating NULL. 373 // 374 status = RtlUShortAdd(imageName.Length, sizeof(UNICODE_NULL), &size); 375 if (!NT_SUCCESS(status)) { 376 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 377 "ERROR: size computation failed, %!status!\n", status); 378 goto cleanUp; 379 } 380 381 // 382 // allocate a buffer to hold Unicode string + null char. 383 // 384 ImageName->Buffer = (PWCH) FxPoolAllocate(FxDriverGlobals, PagedPool, size); 385 386 if (ImageName->Buffer == NULL) { 387 status = STATUS_INSUFFICIENT_RESOURCES; 388 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 389 "ERROR: ExAllocatePoolWithTag failed, %!status!\n", status); 390 goto cleanUp; 391 } 392 393 RtlZeroMemory(ImageName->Buffer, size); 394 ImageName->Length = 0x0; 395 ImageName->MaximumLength = size; 396 397 HRESULT hr = StringCbCopy(ImageName->Buffer, size, imageName.Buffer); 398 if (FAILED(hr)) { 399 status = STATUS_UNSUCCESSFUL; 400 DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER, 401 "ERROR: failed to copy string buffer, HRESULT 0x%x, %!status!\n", 402 hr, status); 403 goto cleanUp; 404 } 405 406 // 407 // The copy cannot fail since we setup the buffer to hold enough space for 408 // the contents of the ImagePath value. 409 // 410 ASSERT(NT_SUCCESS(status)); 411 412 cleanUp: 413 414 if (!NT_SUCCESS(status)) { 415 if (ImageName->Buffer != NULL) { 416 FxPoolFree(ImageName->Buffer); 417 RtlInitUnicodeString(ImageName, NULL); 418 } 419 } 420 421 if (dataBuffer != NULL) { 422 FxPoolFree(dataBuffer); 423 } 424 425 return status; 426 } 427