1 /*++
2 
3 Copyright (c) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     FxBugcheckCallback.cpp
8 
9 Abstract:
10 
11     This module contains the bugcheck callback functions for determining whether
12     a wdf driver caused a bugcheck and for capturing the IFR data to the mini-
13     dump file.
14 
15 Revision History:
16 
17 
18 
19 --*/
20 
21 
22 #include "fxcorepch.hpp"
23 #include "fxifr.h"
24 #include "fxifrkm.h"     // kernel mode only IFR definitions
25 #include "fxldr.h"
26 #include "fxbugcheck.h"
27 
28 #include <aux_klib.h>
29 
30 //
31 // Disable warnings of features used by the standard headers
32 //
33 // Disable warning C4115: named type definition in parentheses
34 // Disable warning C4200: nonstandard extension used : zero-sized array in struct/union
35 // Disable warning C4201: nonstandard extension used : nameless struct/union
36 // Disable warning C4214: nonstandard extension used : bit field types other than int
37 //
38 // #pragma warning(disable:4115 4200 4201 4214)
39 #include <ntimage.h>
40 // #pragma warning(default:4115 4200 4201 4214)
41 
42 
43 extern "C" {
44 
45 //
46 // Private methods.
47 //
48 _Must_inspect_result_
49 BOOLEAN
50 FxpIsAddressKnownToWdf(
51     __in PVOID Address,
52     __in PFX_DRIVER_GLOBALS FxDriverGlobals
53     );
54 
55 _Must_inspect_result_
56 NTSTATUS
57 FxpGetImageBase(
58     __in  PDRIVER_OBJECT DriverObject,
59     __out PVOID* ImageBase,
60     __out PULONG ImageSize
61     );
62 
63 _Must_inspect_result_
64 BOOLEAN
65 FxpBugCheckCallbackFilter(
66     __inout PFX_DRIVER_GLOBALS FxDriverGlobals
67     );
68 
69 KBUGCHECK_REASON_CALLBACK_ROUTINE FxpBugCheckCallback;
70 
71 KBUGCHECK_REASON_CALLBACK_ROUTINE FxpLibraryBugCheckCallback;
72 
73 _Must_inspect_result_
74 BOOLEAN
FxpIsAddressKnownToWdf(__in PVOID Address,__in PFX_DRIVER_GLOBALS FxDriverGlobals)75 FxpIsAddressKnownToWdf(
76     __in PVOID Address,
77     __in PFX_DRIVER_GLOBALS FxDriverGlobals
78     )
79 /*++
80 
81 Routine Description:
82 
83     This routine will check the address to determine if it falls within the
84     image of a WDF component (Library, Client driver or Class Extension).
85     This is accomplished by traversing the WdfLdrGlobals.LoadedModuleList
86     and comparing each image start/end address with Address.
87 
88 Arguments:
89 
90     PVOID Address - The address to be checked, and it need not be currently
91         paged in. In other words, it is NOT used in this routine as an
92         address, but more as a simple scaler into a 4GB (x86) array.
93 
94         NOTE - Do not attempt to validatate Address, say via MmIsAddressValid(Address).
95                Address's memory could be paged out, but Address is still valid.
96 
97     PFX_DRIVER_GLOBALS FxDriverGlobals - Driver's globals.
98 
99 Return Value:
100 
101     TRUE indicates something was found, either library, client or both.
102     FALSE indicates either not found or invalid parameters.
103 
104 --*/
105 {
106 
107     if (NULL == Address || NULL == FxDriverGlobals) {
108         return FALSE;
109     }
110 
111     if (Address >= FxDriverGlobals->ImageAddress &&
112         Address < WDF_PTR_ADD_OFFSET(FxDriverGlobals->ImageAddress,
113                                      FxDriverGlobals->ImageSize)) {
114         return TRUE;
115     }
116 
117     return FALSE;
118 }
119 
120 _Must_inspect_result_
121 NTSTATUS
FxpGetImageBase(__in PDRIVER_OBJECT DriverObject,__out PVOID * ImageBase,__out PULONG ImageSize)122 FxpGetImageBase(
123     __in  PDRIVER_OBJECT DriverObject,
124     __out PVOID* ImageBase,
125     __out PULONG ImageSize
126     )
127 {
128     NTSTATUS status = STATUS_UNSUCCESSFUL;
129     ULONG modulesSize = 0;
130     AUX_MODULE_EXTENDED_INFO* modules = NULL;
131     AUX_MODULE_EXTENDED_INFO* module;
132     PVOID addressInImage = NULL;
133     ULONG numberOfModules;
134     ULONG i;
135 
136     //
137     // Basic validation.
138     //
139     if (NULL == DriverObject || NULL == ImageBase || NULL == ImageSize) {
140         status = STATUS_INVALID_PARAMETER;
141         goto exit;
142     }
143 
144     //
145     // Get the address of a well known entry in the Image.
146     //
147     addressInImage = (PVOID) DriverObject->DriverStart;
148     ASSERT(addressInImage != NULL);
149 
150     //
151     // Initialize the AUX Kernel Library.
152     //
153     status = AuxKlibInitialize();
154     if (!NT_SUCCESS(status)) {
155         goto exit;
156     }
157 
158     //
159     // Get size of area needed for loaded modules.
160     //
161     status = AuxKlibQueryModuleInformation(&modulesSize,
162                                            sizeof(AUX_MODULE_EXTENDED_INFO),
163                                            NULL);
164 
165     if (!NT_SUCCESS(status) || (0 == modulesSize)) {
166         goto exit;
167     }
168 
169     numberOfModules = modulesSize / sizeof(AUX_MODULE_EXTENDED_INFO);
170 
171     //
172     // Allocate returned-sized memory for the modules area.
173     //
174     modules = (AUX_MODULE_EXTENDED_INFO*) ExAllocatePoolWithTag(PagedPool,
175                                                                 modulesSize,
176                                                                 '30LW');
177     if (NULL == modules) {
178         status = STATUS_INSUFFICIENT_RESOURCES;
179         goto exit;
180     }
181 
182     //
183     // Request the modules array be filled with module information.
184     //
185     status = AuxKlibQueryModuleInformation(&modulesSize,
186                                            sizeof(AUX_MODULE_EXTENDED_INFO),
187                                            modules);
188 
189     if (!NT_SUCCESS(status)) {
190         goto exit;
191     }
192 
193     //
194     // Traverse list, searching for the well known address in Image for which the
195     // module's Image Base Address is in its range.
196     //
197     module = modules;
198 
199     for (i=0; i < numberOfModules; i++) {
200 
201         if (addressInImage >= module->BasicInfo.ImageBase &&
202             addressInImage < WDF_PTR_ADD_OFFSET(module->BasicInfo.ImageBase,
203                                                 module->ImageSize)) {
204 
205             *ImageBase = module->BasicInfo.ImageBase;
206             *ImageSize = module->ImageSize;
207 
208             status = STATUS_SUCCESS;
209             goto exit;
210         }
211         module++;
212     }
213 
214     status = STATUS_NOT_FOUND;
215 
216 exit:
217 
218     if (modules != NULL) {
219         ExFreePool(modules);
220         modules = NULL;
221     }
222 
223     return status;
224 }
225 
226 _Must_inspect_result_
227 BOOLEAN
FxpBugCheckCallbackFilter(__inout PFX_DRIVER_GLOBALS FxDriverGlobals)228 FxpBugCheckCallbackFilter(
229     __inout PFX_DRIVER_GLOBALS FxDriverGlobals
230     )
231 /*++
232 
233 Routine Description:
234 
235     This routine evaluates whether the driver's IFR data has to be written to
236     the mini-dump file.
237 
238 Arguments:
239 
240     FxDriverGlobals - The driver globals of the wdf driver.
241 
242 Return Value:
243 
244     TRUE  -  Indicates this driver's IFR log is to be captured in dump.
245     FALSE -  Indicates this driver's log is (probably) not of interest.
246 
247 --*/
248 {
249     PVOID codeAddr = NULL;
250     BOOLEAN found = FALSE;
251     KBUGCHECK_DATA bugCheckData = {0};
252 
253     if (FxDriverGlobals->FxForceLogsInMiniDump) {
254         return TRUE;
255     }
256 
257     //
258     // Retrieve the bugcheck parameters.
259     //
260     bugCheckData.BugCheckDataSize = sizeof(KBUGCHECK_DATA);
261     AuxKlibGetBugCheckData(&bugCheckData);
262 
263     //
264     // Check whether the code address that caused the bugcheck is from this wdf
265     // driver.
266     //
267     switch (bugCheckData.BugCheckCode) {
268 
269     case KERNEL_APC_PENDING_DURING_EXIT:                    // 0x20
270         codeAddr = (PVOID)bugCheckData.Parameter1;
271         found = FxpIsAddressKnownToWdf(codeAddr, FxDriverGlobals);
272         break;
273 
274     case KMODE_EXCEPTION_NOT_HANDLED:                       // 0x1E
275     case SYSTEM_THREAD_EXCEPTION_NOT_HANDLED:               // 0x7E
276     case KERNEL_MODE_EXCEPTION_NOT_HANDLED:                 // 0x8E
277         codeAddr = (PVOID)bugCheckData.Parameter2;
278         found = FxpIsAddressKnownToWdf(codeAddr, FxDriverGlobals);
279         break;
280 
281     case PAGE_FAULT_IN_NONPAGED_AREA:                       // 0x50
282         codeAddr = (PVOID)bugCheckData.Parameter3;
283         found = FxpIsAddressKnownToWdf(codeAddr, FxDriverGlobals);
284         break;
285 
286     case IRQL_NOT_LESS_OR_EQUAL:                            // 0xA
287     case DRIVER_IRQL_NOT_LESS_OR_EQUAL:                     // 0xD1
288         codeAddr = (PVOID)bugCheckData.Parameter4;
289         found = FxpIsAddressKnownToWdf(codeAddr, FxDriverGlobals);
290         break;
291     }
292 
293     //
294     // If the code address was found in the wdf driver, then set the flag in the
295     // driver globals to indicate that the IFR data has to be written to the
296     // mini-dump.
297     //
298     if (found) {
299         FxDriverGlobals->FxForceLogsInMiniDump = TRUE;
300     }
301     return found;
302 }
303 
304 VOID
305 STDCALL
FxpBugCheckCallback(__in KBUGCHECK_CALLBACK_REASON Reason,__in PKBUGCHECK_REASON_CALLBACK_RECORD Record,__inout PVOID ReasonSpecificData,__in ULONG ReasonSpecificLength)306 FxpBugCheckCallback(
307     __in    KBUGCHECK_CALLBACK_REASON Reason,
308     __in    PKBUGCHECK_REASON_CALLBACK_RECORD Record,
309     __inout PVOID ReasonSpecificData,
310     __in    ULONG ReasonSpecificLength
311     )
312 
313 /*++
314 
315 Routine Description:
316 
317     BugCheck callback routine for WDF
318 
319 Arguments:
320 
321     Reason               - Must be KbCallbackSecondaryData
322     Record               - Supplies the bugcheck record previously registered
323     ReasonSpecificData   - Pointer to KBUGCHECK_SECONDARY_DUMP_DATA
324     ReasonSpecificLength - Sizeof(ReasonSpecificData)
325 
326 Return Value:
327 
328     None
329 
330 Notes:
331     When a bugcheck happens the kernel bugcheck processor will make two passes
332     of all registered BugCheckCallbackRecord routines.  The first pass, called
333     the "sizing pass" essentially queries all the callbacks to collect the
334     total size of the secondary dump data. In the second pass the actual data
335     is captured to the dump.
336 
337 --*/
338 
339 {
340     PKBUGCHECK_SECONDARY_DUMP_DATA dumpData;
341     PFX_DRIVER_GLOBALS fxDriverGlobals;
342     ULONG logSize;
343     BOOLEAN writeLog = FALSE;
344 
345     UNREFERENCED_PARAMETER(Reason);
346     UNREFERENCED_PARAMETER(ReasonSpecificLength);
347 
348     ASSERT(ReasonSpecificLength >= sizeof(KBUGCHECK_SECONDARY_DUMP_DATA));
349     ASSERT(Reason == KbCallbackSecondaryDumpData);
350 
351     dumpData = (PKBUGCHECK_SECONDARY_DUMP_DATA) ReasonSpecificData;
352 
353     //
354     // See if the IFR's minimum amount of data can fit in the dump
355     //
356     if (dumpData->MaximumAllowed < FxIFRMinLogSize) {
357         return;
358     }
359 
360     //
361     // Get the driver globals containing the bugcheck callback record.
362     //
363     fxDriverGlobals = CONTAINING_RECORD(Record,
364                                         FX_DRIVER_GLOBALS,
365                                         BugCheckCallbackRecord);
366 
367     //
368     // If there is not IFR data, then this bugcheck callback does not have any
369     // data to write to the minidump.
370     //
371     if (NULL == fxDriverGlobals->WdfLogHeader) {
372         return;
373     }
374 
375     //
376     // Size is the length of the buffer which we log to. It should also include
377     // the size of the header (which was subtracted out in FxIFRStart).
378     //
379     logSize = ((PWDF_IFR_HEADER) fxDriverGlobals->WdfLogHeader)->Size +
380               sizeof(WDF_IFR_HEADER);
381 
382     ASSERT(FxIFRMinLogSize <= logSize && logSize <= FxIFRMaxLogSize);
383 
384     //
385     // Check whether log can fit in the dump.
386     //
387     if (logSize > dumpData->MaximumAllowed) {
388         return;
389     }
390 
391     //
392     // Check whether this log is the one to copy.
393     //
394     if (FxpBugCheckCallbackFilter(fxDriverGlobals)) {
395         //
396         // Let everyone know that we got the best match.
397         //
398         FxLibraryGlobals.BestDriverForDumpLog = fxDriverGlobals;
399 
400         //
401         // Exact match driver.
402         //
403         writeLog = TRUE;
404     }
405     else if (NULL == FxLibraryGlobals.BestDriverForDumpLog) {
406         //
407         // So far we didn't get a perfect match. Make a best effort
408         // to find a good candidate for the driver dump log.
409         //
410         if (fxDriverGlobals->FxTrackDriverForMiniDumpLog &&
411             FxLibraryGlobals.DriverTracker.GetCurrentTrackedDriver() ==
412                 fxDriverGlobals) {
413             //
414             // Best effort match driver.
415             //
416             writeLog = TRUE;
417         }
418     }
419 
420     if (writeLog) {
421         dumpData->OutBuffer = fxDriverGlobals->WdfLogHeader;
422         dumpData->OutBufferLength  = logSize;
423         dumpData->Guid = WdfDumpGuid;
424     }
425 }
426 
427 VOID
FxRegisterBugCheckCallback(__inout PFX_DRIVER_GLOBALS FxDriverGlobals,__in PDRIVER_OBJECT DriverObject)428 FxRegisterBugCheckCallback(
429     __inout PFX_DRIVER_GLOBALS FxDriverGlobals,
430     __in    PDRIVER_OBJECT DriverObject
431     )
432 {
433     UNICODE_STRING funcName;
434     PKBUGCHECK_REASON_CALLBACK_RECORD callbackRecord;
435     PFN_KE_REGISTER_BUGCHECK_REASON_CALLBACK funcPtr;
436     BOOLEAN enableDriverTracking;
437 
438     //
439     // If any problem during this setup, disable driver tracking.
440     //
441     enableDriverTracking = FxDriverGlobals->FxTrackDriverForMiniDumpLog;
442     FxDriverGlobals->FxTrackDriverForMiniDumpLog = FALSE;
443 
444     //
445     // Zero out callback record.
446     //
447     callbackRecord = &FxDriverGlobals->BugCheckCallbackRecord;
448     RtlZeroMemory(callbackRecord, sizeof(KBUGCHECK_REASON_CALLBACK_RECORD));
449 
450     //
451     // Get the Image base address and size before registering the bugcheck
452     // callbacks. If the image base address and size cannot be computed,
453     // then the bugcheck callbacks depend on these values being properly
454     // set.
455     //
456     FxDriverGlobals->ImageAddress = NULL;
457     FxDriverGlobals->ImageSize = 0;
458 
459     if (!NT_SUCCESS(FxpGetImageBase(DriverObject,
460                                    &FxDriverGlobals->ImageAddress,
461                                    &FxDriverGlobals->ImageSize))) {
462         goto Done;
463     }
464 
465 
466 
467 
468 
469 
470 
471 
472 
473 
474     //
475     // The KeRegisterBugCheckReasonCallback exists for xp sp1 and above. So
476     // check whether this function is defined on the current OS and register
477     // for the bugcheck callback only if this function is defined.
478     //
479     RtlInitUnicodeString(&funcName, L"KeRegisterBugCheckReasonCallback");
480     funcPtr = (PFN_KE_REGISTER_BUGCHECK_REASON_CALLBACK)
481         MmGetSystemRoutineAddress(&funcName);
482 
483     if (NULL == funcPtr) {
484         goto Done;
485     }
486 
487     //
488     // Register this driver with driver tracker.
489     //
490     if (enableDriverTracking) {
491         if (NT_SUCCESS(FxLibraryGlobals.DriverTracker.Register(
492                             FxDriverGlobals))) {
493             FxDriverGlobals->FxTrackDriverForMiniDumpLog = TRUE;
494         }
495     }
496 
497     //
498     // Initialize the callback record.
499     //
500     KeInitializeCallbackRecord(callbackRecord);
501 
502     //
503     // Register the bugcheck callback.
504     //
505     funcPtr(callbackRecord,
506             FxpBugCheckCallback,
507             KbCallbackSecondaryDumpData,
508             (PUCHAR)FxDriverGlobals->Public.DriverName);
509 
510     ASSERT(callbackRecord->CallbackRoutine != NULL);
511 
512 Done:;
513 }
514 
515 VOID
FxUnregisterBugCheckCallback(__inout PFX_DRIVER_GLOBALS FxDriverGlobals)516 FxUnregisterBugCheckCallback(
517     __inout PFX_DRIVER_GLOBALS FxDriverGlobals
518     )
519 {
520     UNICODE_STRING funcName;
521     PKBUGCHECK_REASON_CALLBACK_RECORD callbackRecord;
522     PFN_KE_DEREGISTER_BUGCHECK_REASON_CALLBACK funcPtr;
523 
524     callbackRecord = &FxDriverGlobals->BugCheckCallbackRecord;
525     if (NULL == callbackRecord->CallbackRoutine) {
526         goto Done;
527     }
528 
529     //
530     // The KeDeregisterBugCheckReasonCallback exists for xp sp1 and above. So
531     // check whether this function is defined on the current OS and deregister
532     // from the bugcheck callback only if this function is defined.
533     //
534     RtlInitUnicodeString(&funcName, L"KeDeregisterBugCheckReasonCallback");
535     funcPtr = (PFN_KE_DEREGISTER_BUGCHECK_REASON_CALLBACK)
536         MmGetSystemRoutineAddress(&funcName);
537 
538     if (NULL == funcPtr) {
539         goto Done;
540     }
541 
542     funcPtr(callbackRecord);
543     callbackRecord->CallbackRoutine = NULL;
544 
545     //
546     // Deregister this driver with driver tracker.
547     //
548     if (FxDriverGlobals->FxTrackDriverForMiniDumpLog) {
549         FxLibraryGlobals.DriverTracker.Deregister(FxDriverGlobals);
550     }
551 
552 Done:;
553 }
554 
555 VOID
556 STDCALL
FxpLibraryBugCheckCallback(__in KBUGCHECK_CALLBACK_REASON Reason,__in PKBUGCHECK_REASON_CALLBACK_RECORD,__inout PVOID ReasonSpecificData,__in ULONG ReasonSpecificLength)557 FxpLibraryBugCheckCallback(
558     __in    KBUGCHECK_CALLBACK_REASON Reason,
559     __in    PKBUGCHECK_REASON_CALLBACK_RECORD /* Record */,
560     __inout PVOID ReasonSpecificData,
561     __in    ULONG ReasonSpecificLength
562     )
563 
564 /*++
565 
566 Routine Description:
567 
568     Global (framework-library) BugCheck callback routine for WDF
569 
570 Arguments:
571 
572     Reason               - Must be KbCallbackSecondaryData
573     Record               - Supplies the bugcheck record previously registered
574     ReasonSpecificData   - Pointer to KBUGCHECK_SECONDARY_DUMP_DATA
575     ReasonSpecificLength - Sizeof(ReasonSpecificData)
576 
577 Return Value:
578 
579     None
580 
581 Notes:
582     When a bugcheck happens the kernel bugcheck processor will make two passes
583     of all registered BugCheckCallbackRecord routines.  The first pass, called
584     the "sizing pass" essentially queries all the callbacks to collect the
585     total size of the secondary dump data. In the second pass the actual data
586     is captured to the dump.
587 
588 --*/
589 
590 {
591     PKBUGCHECK_SECONDARY_DUMP_DATA  dumpData;
592     ULONG                           dumpSize;
593 
594     UNREFERENCED_PARAMETER(Reason);
595     UNREFERENCED_PARAMETER(ReasonSpecificLength);
596 
597     ASSERT(ReasonSpecificLength >= sizeof(KBUGCHECK_SECONDARY_DUMP_DATA));
598     ASSERT(Reason == KbCallbackSecondaryDumpData);
599 
600     dumpData = (PKBUGCHECK_SECONDARY_DUMP_DATA) ReasonSpecificData;
601     dumpSize = FxLibraryGlobals.BugCheckDriverInfoIndex *
602                 sizeof(FX_DUMP_DRIVER_INFO_ENTRY);
603     //
604     // See if the bugcheck driver info is more than can fit in the dump
605     //
606     if (dumpData->MaximumAllowed < dumpSize) {
607         dumpSize = EXP_ALIGN_DOWN_ON_BOUNDARY(
608                         dumpData->MaximumAllowed,
609                         sizeof(FX_DUMP_DRIVER_INFO_ENTRY));
610     }
611 
612     if (0 == dumpSize) {
613         goto Done;
614     }
615 
616     //
617     // Ok, provide the info about the bugcheck data.
618     //
619     dumpData->OutBuffer = FxLibraryGlobals.BugCheckDriverInfo;
620     dumpData->OutBufferLength  = dumpSize;
621     dumpData->Guid = WdfDumpGuid2;
622 
623 Done:;
624 }
625 
626 VOID
FxInitializeBugCheckDriverInfo()627 FxInitializeBugCheckDriverInfo()
628 {
629     NTSTATUS                                 status;
630     UNICODE_STRING                           funcName;
631     PKBUGCHECK_REASON_CALLBACK_RECORD        callbackRecord;
632     PFN_KE_REGISTER_BUGCHECK_REASON_CALLBACK funcPtr;
633     SIZE_T                                   arraySize;
634     ULONG                                    arrayCount;
635 
636 
637     callbackRecord = &FxLibraryGlobals.BugCheckCallbackRecord;
638     RtlZeroMemory(callbackRecord, sizeof(KBUGCHECK_REASON_CALLBACK_RECORD));
639 
640     FxLibraryGlobals.BugCheckDriverInfoCount = 0;
641     FxLibraryGlobals.BugCheckDriverInfoIndex = 0;
642     FxLibraryGlobals.BugCheckDriverInfo = NULL;
643 
644 
645 
646 
647 
648 
649 
650 
651 
652 
653     //
654     // The KeRegisterBugCheckReasonCallback exists for xp sp1 and above. So
655     // check whether this function is defined on the current OS and register
656     // for the bugcheck callback only if this function is defined.
657     //
658     RtlInitUnicodeString(&funcName, L"KeRegisterBugCheckReasonCallback");
659     funcPtr = (PFN_KE_REGISTER_BUGCHECK_REASON_CALLBACK)
660         MmGetSystemRoutineAddress(&funcName);
661 
662     if (NULL == funcPtr) {
663         goto Done;
664     }
665 
666     arraySize = sizeof(FX_DUMP_DRIVER_INFO_ENTRY) * FX_DUMP_DRIVER_INFO_INCREMENT;
667     arrayCount = FX_DUMP_DRIVER_INFO_INCREMENT;
668 
669     FxLibraryGlobals.BugCheckDriverInfo =
670         (PFX_DUMP_DRIVER_INFO_ENTRY)MxMemory::MxAllocatePoolWithTag(
671                                     NonPagedPool,
672                                     arraySize,
673                                     FX_TAG);
674 
675     if (NULL == FxLibraryGlobals.BugCheckDriverInfo) {
676         goto Done;
677     }
678 
679     FxLibraryGlobals.BugCheckDriverInfoCount = arrayCount;
680 
681     //
682     // Init first entry for the framework.
683     //
684     FxLibraryGlobals.BugCheckDriverInfo[0].FxDriverGlobals = NULL;
685     FxLibraryGlobals.BugCheckDriverInfo[0].Version.Major = __WDF_MAJOR_VERSION;
686     FxLibraryGlobals.BugCheckDriverInfo[0].Version.Minor = __WDF_MINOR_VERSION;
687     FxLibraryGlobals.BugCheckDriverInfo[0].Version.Build = __WDF_BUILD_NUMBER;
688 
689     status = RtlStringCbCopyA(
690                     FxLibraryGlobals.BugCheckDriverInfo[0].DriverName,
691                     sizeof(FxLibraryGlobals.BugCheckDriverInfo[0].DriverName),
692                     WdfLdrType);
693 
694     if (!NT_SUCCESS(status)) {
695         ASSERT(FALSE);
696         FxLibraryGlobals.BugCheckDriverInfo[0].DriverName[0] = '\0';
697     }
698 
699     FxLibraryGlobals.BugCheckDriverInfoIndex++;
700 
701     //
702     // Initialize the callback record.
703     //
704     KeInitializeCallbackRecord(callbackRecord);
705 
706     //
707     // Register the bugcheck callback.
708     //
709     funcPtr(callbackRecord,
710             FxpLibraryBugCheckCallback,
711             KbCallbackSecondaryDumpData,
712             (PUCHAR)WdfLdrType);
713 
714     ASSERT(callbackRecord->CallbackRoutine != NULL);
715 
716 Done:;
717 }
718 
719 VOID
FxUninitializeBugCheckDriverInfo()720 FxUninitializeBugCheckDriverInfo()
721 {
722     UNICODE_STRING                              funcName;
723     PKBUGCHECK_REASON_CALLBACK_RECORD           callbackRecord;
724     PFN_KE_DEREGISTER_BUGCHECK_REASON_CALLBACK  funcPtr;
725 
726     //
727     // Deregister callback.
728     //
729 
730 
731 
732 
733 
734 
735 
736     callbackRecord = &FxLibraryGlobals.BugCheckCallbackRecord;
737     if (NULL == callbackRecord->CallbackRoutine) {
738         goto Done;
739     }
740 
741     //
742     // The KeDeregisterBugCheckReasonCallback exists for xp sp1 and above. So
743     // check whether this function is defined on the current OS and deregister
744     // from the bugcheck callback only if this function is defined.
745     //
746     RtlInitUnicodeString(&funcName, L"KeDeregisterBugCheckReasonCallback");
747     funcPtr = (PFN_KE_DEREGISTER_BUGCHECK_REASON_CALLBACK)
748         MmGetSystemRoutineAddress(&funcName);
749 
750     if (NULL == funcPtr) {
751         goto Done;
752     }
753 
754     funcPtr(callbackRecord);
755     callbackRecord->CallbackRoutine = NULL;
756 
757     //
758     // Release memory.
759     //
760     if (NULL == FxLibraryGlobals.BugCheckDriverInfo) {
761         goto Done;
762     }
763 
764     //
765     // Dynamic KMDF framework is unloading, make sure there is only one entry
766     // left in the driver info array; the framework lib was using this entry
767     // to store its version.
768     //
769     ASSERT(1 == FxLibraryGlobals.BugCheckDriverInfoIndex);
770 
771     FxLibraryGlobals.BugCheckDriverInfoIndex = 0;
772     FxLibraryGlobals.BugCheckDriverInfoCount = 0;
773 
774     MxMemory::MxFreePool(FxLibraryGlobals.BugCheckDriverInfo);
775     FxLibraryGlobals.BugCheckDriverInfo = NULL;
776 
777 Done:;
778 }
779 
780 VOID
FxCacheBugCheckDriverInfo(__in PFX_DRIVER_GLOBALS FxDriverGlobals)781 FxCacheBugCheckDriverInfo(
782     __in PFX_DRIVER_GLOBALS FxDriverGlobals
783     )
784 {
785     KIRQL                       irql;
786     PFX_DUMP_DRIVER_INFO_ENTRY  driverInfo      = NULL;
787     PFX_DUMP_DRIVER_INFO_ENTRY  oldDriverInfo   = NULL;
788     ULONG                       newCount        = 0;
789 
790     //
791     // Clear driver index (0-based). Drivers do not use index 0.
792     //
793     FxDriverGlobals->BugCheckDriverInfoIndex = 0;
794 
795     if (NULL == FxLibraryGlobals.BugCheckDriverInfo) {
796         return;
797     }
798 
799     FxLibraryGlobals.FxDriverGlobalsListLock.Acquire(&irql);
800 
801     //
802     // Make sure we have enough space, else allocate some more memory.
803     //
804     if (FxLibraryGlobals.BugCheckDriverInfoIndex >=
805             FxLibraryGlobals.BugCheckDriverInfoCount) {
806 
807         //
808         // Just exit if no more space in dump buffer.
809         //
810         if ((FX_MAX_DUMP_DRIVER_INFO_COUNT - FX_DUMP_DRIVER_INFO_INCREMENT) <
811              FxLibraryGlobals.BugCheckDriverInfoCount) {
812              ASSERT(FALSE);
813              goto Done;
814         }
815 
816         newCount = FxLibraryGlobals.BugCheckDriverInfoCount +
817                       FX_DUMP_DRIVER_INFO_INCREMENT;
818 
819         //
820         // Allocate new buffer to hold driver info.
821         //
822         driverInfo = (PFX_DUMP_DRIVER_INFO_ENTRY)MxMemory::MxAllocatePoolWithTag(
823                                 NonPagedPool,
824                                 sizeof(FX_DUMP_DRIVER_INFO_ENTRY)* newCount,
825                                 FX_TAG);
826 
827         if (NULL == driverInfo) {
828             goto Done;
829         }
830 
831         //
832         // Copy data from old to new buffer.
833         //
834         RtlCopyMemory(driverInfo,
835                       FxLibraryGlobals.BugCheckDriverInfo,
836                       FxLibraryGlobals.BugCheckDriverInfoCount *
837                         sizeof(FX_DUMP_DRIVER_INFO_ENTRY));
838         //
839         // Ok, replace global pointer and its count.
840         //
841         oldDriverInfo = FxLibraryGlobals.BugCheckDriverInfo;
842         FxLibraryGlobals.BugCheckDriverInfo = driverInfo;
843         FxLibraryGlobals.BugCheckDriverInfoCount = newCount;
844         driverInfo = NULL; // just in case.
845 
846         //
847         // Free old memory.
848         //
849         MxMemory::MxFreePool(oldDriverInfo);
850         oldDriverInfo = NULL;
851     }
852 
853     ASSERT(FxLibraryGlobals.BugCheckDriverInfoIndex <
854                 FxLibraryGlobals.BugCheckDriverInfoCount);
855     //
856     // Compute ptr to free entry.
857     //
858     driverInfo = FxLibraryGlobals.BugCheckDriverInfo +
859                     FxLibraryGlobals.BugCheckDriverInfoIndex;
860     //
861     // Cache some of this driver's info.
862     //
863     driverInfo->FxDriverGlobals = FxDriverGlobals;
864     driverInfo->Version = FxDriverGlobals->WdfBindInfo->Version;
865 
866     C_ASSERT(sizeof(driverInfo->DriverName) ==
867                 sizeof(FxDriverGlobals->Public.DriverName));
868     RtlCopyMemory(driverInfo->DriverName,
869                   FxDriverGlobals->Public.DriverName,
870                   sizeof(driverInfo->DriverName));
871     driverInfo->DriverName[ARRAY_SIZE(driverInfo->DriverName) - 1] = '\0';
872 
873     //
874     // Cache this index for later when the driver is removed.
875     //
876     FxDriverGlobals->BugCheckDriverInfoIndex =
877         FxLibraryGlobals.BugCheckDriverInfoIndex;
878 
879     //
880     // Update global index.
881     //
882     FxLibraryGlobals.BugCheckDriverInfoIndex++;
883 
884 Done:
885 
886     FxLibraryGlobals.FxDriverGlobalsListLock.Release(irql);
887 }
888 
889 VOID
FxPurgeBugCheckDriverInfo(__in PFX_DRIVER_GLOBALS FxDriverGlobals)890 FxPurgeBugCheckDriverInfo(
891     __in PFX_DRIVER_GLOBALS FxDriverGlobals
892     )
893 {
894     KIRQL                       irql;
895     PFX_DUMP_DRIVER_INFO_ENTRY  driverInfo  = NULL;
896     ULONG                       driverIndex = 0;
897     ULONG                       shiftCount  = 0;
898     ULONG                       i           = 0;
899 
900     FxLibraryGlobals.FxDriverGlobalsListLock.Acquire(&irql);
901 
902     //
903     // Zero means 'not used'.
904     //
905     driverIndex = FxDriverGlobals->BugCheckDriverInfoIndex;
906     if (0 == driverIndex) {
907         goto Done;
908     }
909 
910     //
911     // Check if feature is not supported.
912     //
913     if (NULL == FxLibraryGlobals.BugCheckDriverInfo) {
914         ASSERT(FALSE); // driverIndex > 0 with NULL array?
915         goto Done;
916     }
917 
918 
919     ASSERT(FxLibraryGlobals.BugCheckDriverInfoIndex <=
920             FxLibraryGlobals.BugCheckDriverInfoCount);
921 
922     //
923     // Index boundary validation.
924     //
925     if (driverIndex >= FxLibraryGlobals.BugCheckDriverInfoIndex) {
926         ASSERT(FALSE);
927         goto Done;
928     }
929 
930     //
931     // Compute ptr to driver info.
932     //
933     driverInfo = FxLibraryGlobals.BugCheckDriverInfo + driverIndex;
934 
935     //
936     // Double-check that this is the same driver.
937     //
938     if (driverInfo->FxDriverGlobals != FxDriverGlobals) {
939         ASSERT(FALSE);
940         goto Done;
941     }
942 
943     //
944     // Shift memory to fill hole and update global free index.
945     //
946     shiftCount = FxLibraryGlobals.BugCheckDriverInfoIndex - driverIndex - 1;
947     if (shiftCount > 0) {
948         RtlMoveMemory(driverInfo,
949                       driverInfo + 1,
950                       shiftCount * sizeof(FX_DUMP_DRIVER_INFO_ENTRY));
951     }
952 
953     FxLibraryGlobals.BugCheckDriverInfoIndex--;
954 
955     //
956     // Update cached index for all 'shifted' drivers.
957     //
958     for (i = driverIndex; i < FxLibraryGlobals.BugCheckDriverInfoIndex; ++i) {
959         FxLibraryGlobals.BugCheckDriverInfo[i].FxDriverGlobals->
960                 BugCheckDriverInfoIndex--;
961     }
962 
963 Done:
964 
965     FxLibraryGlobals.FxDriverGlobalsListLock.Release(irql);
966 }
967 
968 //
969 // Driver usage tracker functions
970 //
971 NTSTATUS
Initialize()972 FX_DRIVER_TRACKER_CACHE_AWARE::Initialize()
973 {
974     //
975     // Global initialization. It balances the global Uninitialize function.
976     // The real init is actually done by the Register function the first time
977     // a driver registers with the device tracker.
978     //
979     m_DriverUsage   = NULL;
980     m_PoolToFree    = NULL;
981     m_EntrySize     = 0;
982     m_Number        = 0;
983 
984     return STATUS_SUCCESS;
985 }
986 
987 VOID
Uninitialize()988 FX_DRIVER_TRACKER_CACHE_AWARE::Uninitialize()
989 {
990     if (m_PoolToFree != NULL) {
991 
992 #ifdef DBG
993     for (ULONG index = 0; index < m_Number; ++index) {
994         PFX_DRIVER_TRACKER_ENTRY driverUsage = NULL;
995         driverUsage = GetProcessorDriverEntryRef(index);
996         ASSERT(NULL == driverUsage->FxDriverGlobals);
997     }
998 #endif
999         MxMemory::MxFreePool(m_PoolToFree);
1000         m_PoolToFree = NULL;
1001     }
1002 
1003     m_DriverUsage   = NULL;
1004     m_Number        = 0;
1005 }
1006 
1007 NTSTATUS
Register(__in PFX_DRIVER_GLOBALS)1008 FX_DRIVER_TRACKER_CACHE_AWARE::Register(
1009     __in PFX_DRIVER_GLOBALS /* FxDriverGlobals */
1010     )
1011 {
1012     NTSTATUS                    status      = STATUS_UNSUCCESSFUL;
1013     ULONG                       paddedSize  = 0;
1014     ULONG                       index       = 0;
1015     PFX_DRIVER_TRACKER_ENTRY    pool        = NULL;
1016     PFX_DRIVER_TRACKER_ENTRY    driverUsage = NULL;
1017     UNICODE_STRING              funcName;
1018     PVOID                       funcPtr     = NULL;
1019 
1020     //
1021     // Nothing to do if tracker is already initialized. No need for a lock
1022     // here since PnP already serializes driver initialization.
1023     //
1024     if (m_PoolToFree != NULL) {
1025         ASSERT(m_DriverUsage != NULL && m_Number != 0);
1026         status = STATUS_SUCCESS;
1027         goto Done;
1028     }
1029 
1030     //
1031     // Intialize the procgrp down level library.
1032     //
1033     // WdmlibProcgrpInitialize(); __REACTOS__ : haha we don't support ProcGrp
1034 
1035     //
1036     // Capture maximum number of processors.
1037     //
1038     RtlInitUnicodeString(&funcName, L"KeQueryMaximumProcessorCountEx");
1039     funcPtr = MmGetSystemRoutineAddress(&funcName);
1040     if (funcPtr != NULL) {
1041         //
1042         // Win 7 and forward.
1043         //
1044         m_Number = ((PFN_KE_QUERY_MAXIMUM_PROCESSOR_COUNT_EX)funcPtr)(
1045                             ALL_PROCESSOR_GROUPS);
1046     }
1047     else {
1048         RtlInitUnicodeString(&funcName, L"KeQueryMaximumProcessorCount");
1049         funcPtr = MmGetSystemRoutineAddress(&funcName);
1050         if (funcPtr != NULL) {
1051             //
1052             // Windows Server 2008.
1053             //
1054             m_Number = ((PFN_KE_QUERY_MAXIMUM_PROCESSOR_COUNT)funcPtr)();
1055         }
1056         else {
1057             if ((5 == FxLibraryGlobals.OsVersionInfo.dwMajorVersion &&
1058                  0 <  FxLibraryGlobals.OsVersionInfo.dwMinorVersion) ||
1059                 (6 == FxLibraryGlobals.OsVersionInfo.dwMajorVersion &&
1060                  0 == FxLibraryGlobals.OsVersionInfo.dwMinorVersion)){
1061                 //
1062                 // XP (Major=5, Minor>0) and Vista (Major=6, Minor=0).
1063                 //
1064                 m_Number = (ULONG)(*((CCHAR *)&KeNumberProcessors));
1065             }
1066             else {
1067                 //
1068                 // This feature is not supported for Windows 2000.
1069                 //
1070                 ASSERT(FALSE);
1071                 status = STATUS_NOT_SUPPORTED;
1072                 goto Done;
1073             }
1074         }
1075     }
1076 
1077     //
1078     // Validate upper bound.
1079     //
1080     if (m_Number > FX_DRIVER_TRACKER_MAX_CPUS) {
1081         status = STATUS_NOT_SUPPORTED;
1082         goto Done;
1083     }
1084 
1085     //
1086     // Determine padded size of each tracking entry structure.
1087     //
1088     if (m_Number > 1 ) {
1089         RtlInitUnicodeString(&funcName, L"KeGetRecommendedSharedDataAlignment");
1090         funcPtr = MmGetSystemRoutineAddress(&funcName);
1091 
1092         if (funcPtr != NULL) {
1093             //
1094             //XP and forward
1095             //
1096             paddedSize = ((PFN_KE_GET_RECOMMENDED_SHARED_DATA_ALIGNMENT)funcPtr)();
1097             ASSERT ((paddedSize & (paddedSize - 1)) == 0);
1098         }
1099         else {
1100             //
1101             // This feature is not supported for Windows 2000.
1102             //
1103             status = STATUS_NOT_SUPPORTED;
1104             goto Done;
1105         }
1106     }
1107     else {
1108         paddedSize = sizeof(FX_DRIVER_TRACKER_ENTRY);
1109     }
1110 
1111     ASSERT(sizeof(FX_DRIVER_TRACKER_ENTRY) <= paddedSize);
1112 
1113     //
1114     // Remember the size.
1115     //
1116     m_EntrySize = paddedSize;
1117 
1118     pool = (PFX_DRIVER_TRACKER_ENTRY)MxMemory::MxAllocatePoolWithTag(
1119                                         NonPagedPool,
1120                                         paddedSize * m_Number,
1121                                         FX_TAG);
1122     if (NULL == pool) {
1123         status = STATUS_INSUFFICIENT_RESOURCES;
1124         goto Done;
1125     }
1126 
1127     //
1128     // Check if pool is aligned if this is a multi-proc.
1129     //
1130     if ((m_Number > 1) && !EXP_IS_PTR_ALIGNED_ON_BOUNDARY(pool, paddedSize)) {
1131         //
1132         // We will allocate a padded size, free the old pool.
1133         //
1134         ExFreePool(pool);
1135 
1136         //
1137         // Allocate enough padding so we can start the refs on an aligned boundary
1138         //
1139         pool = (PFX_DRIVER_TRACKER_ENTRY)MxMemory::MxAllocatePoolWithTag(
1140                                         NonPagedPool,
1141                                         paddedSize * m_Number + paddedSize,
1142                                         FX_TAG);
1143         if (NULL == pool) {
1144             status = STATUS_INSUFFICIENT_RESOURCES;
1145             goto Done;
1146         }
1147 
1148         driverUsage = (PFX_DRIVER_TRACKER_ENTRY)
1149                             EXP_ALIGN_UP_PTR_ON_BOUNDARY(pool, paddedSize);
1150 
1151     }
1152     else {
1153         //
1154         // Already aligned pool.
1155         //
1156         driverUsage = pool;
1157     }
1158 
1159     //
1160     // Remember the pool block to free.
1161     //
1162     m_PoolToFree  = pool;
1163     m_DriverUsage = driverUsage;
1164 
1165     //
1166     // Init driver usage entries.
1167     //
1168     for (index = 0; index < m_Number; ++index) {
1169         driverUsage = GetProcessorDriverEntryRef(index);
1170         driverUsage->FxDriverGlobals = NULL;
1171     }
1172 
1173     //
1174     // All done.
1175     //
1176     status = STATUS_SUCCESS;
1177 
1178 Done:
1179     return status;
1180 }
1181 
1182 VOID
Deregister(__in PFX_DRIVER_GLOBALS FxDriverGlobals)1183 FX_DRIVER_TRACKER_CACHE_AWARE::Deregister(
1184     __in PFX_DRIVER_GLOBALS FxDriverGlobals
1185     )
1186 {
1187     ULONG                     index       = 0;
1188     PFX_DRIVER_TRACKER_ENTRY  driverUsage = NULL;
1189 
1190     //
1191     // Cleanup any refs to this driver's globals.
1192     //
1193     if (m_PoolToFree != NULL) {
1194 
1195         ASSERT(m_DriverUsage != NULL && m_Number != 0);
1196 
1197         for (index = 0; index < m_Number; ++index) {
1198             driverUsage = GetProcessorDriverEntryRef(index);
1199             if (driverUsage->FxDriverGlobals == FxDriverGlobals) {
1200                 driverUsage->FxDriverGlobals = NULL;
1201             }
1202         }
1203     }
1204 }
1205 
1206 } // extern "C"
1207