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