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
AllocAndInitializeTelemetryContext(_In_ PFX_TELEMETRY_CONTEXT * TelemetryContext)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
RegisterTelemetryProvider(VOID)76 RegisterTelemetryProvider(
77 VOID
78 )
79 {
80 EventRegisterMicrosoft_Windows_DriverFrameworks_UserMode();
81
82 TraceLoggingRegister(g_TelemetryProvider);
83 }
84
85 VOID
UnregisterTelemetryProvider(VOID)86 UnregisterTelemetryProvider(
87 VOID
88 )
89 {
90 EventUnregisterMicrosoft_Windows_DriverFrameworks_UserMode();
91
92 TraceLoggingUnregister(g_TelemetryProvider);
93 }
94
95 VOID
LogDeviceStartTelemetryEvent(_In_ PFX_DRIVER_GLOBALS Globals,_In_opt_ FxDevice * Fdo)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
LogDriverInfoStream(_In_ PFX_DRIVER_GLOBALS DriverGlobals,_In_ FxDevice * Fdo)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
GetDriverInfo(_In_ FxDevice * Fdo,_In_ PUMDF_DRIVER_REGSITRY_INFO RegInfo,_Out_ UFxTelemetryDriverInfo * DriverInfo)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
GetImageName(_In_ PFX_DRIVER_GLOBALS FxDriverGlobals,_Out_ PUNICODE_STRING ImageName)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