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