xref: /reactos/drivers/storage/class/classpnp/utils.c (revision 34593d93)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 2010
4 
5 Module Name:
6 
7     utils.c
8 
9 Abstract:
10 
11     SCSI class driver routines
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 
25 #include "classp.h"
26 #include "debug.h"
27 #include <ntiologc.h>
28 
29 
30 #ifdef DEBUG_USE_WPP
31 #include "utils.tmh"
32 #endif
33 
34 //
35 // Constant value used in firmware upgrade process.
36 //
37 #define FIRMWARE_ACTIVATE_TIMEOUT_VALUE        30
38 
39 
40 #ifdef ALLOC_PRAGMA
41     #pragma alloc_text(PAGE, ClassGetDeviceParameter)
42     #pragma alloc_text(PAGE, ClassScanForSpecial)
43     #pragma alloc_text(PAGE, ClassSetDeviceParameter)
44     #pragma alloc_text(PAGE, ClasspMyStringMatches)
45     #pragma alloc_text(PAGE, ClasspDeviceCopyOffloadProperty)
46     #pragma alloc_text(PAGE, ClasspValidateOffloadSupported)
47     #pragma alloc_text(PAGE, ClasspValidateOffloadInputParameters)
48 #endif
49 
50 // custom string match -- careful!
ClasspMyStringMatches(_In_opt_z_ PCHAR StringToMatch,_In_z_ PCHAR TargetString)51 BOOLEAN ClasspMyStringMatches(_In_opt_z_ PCHAR StringToMatch, _In_z_ PCHAR TargetString)
52 {
53     ULONG length;  // strlen returns an int, not size_t (!)
54     PAGED_CODE();
55     NT_ASSERT(TargetString);
56     // if no match requested, return TRUE
57     if (StringToMatch == NULL) {
58         return TRUE;
59     }
60     // cache the string length for efficiency
61     length = (ULONG)strlen(StringToMatch);
62     // ZERO-length strings may only match zero-length strings
63     if (length == 0) {
64         return (strlen(TargetString) == 0);
65     }
66     // strncmp returns zero if the strings match
67     return (strncmp(StringToMatch, TargetString, length) == 0);
68 }
69 
70 
_IRQL_requires_max_(PASSIVE_LEVEL)71 _IRQL_requires_max_(PASSIVE_LEVEL)
72 VOID
73 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
74 ClassGetDeviceParameter(
75     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
76     _In_opt_ PWSTR SubkeyName,
77     _In_ PWSTR ParameterName,
78     _Inout_ PULONG ParameterValue  // also default value
79     )
80 {
81     NTSTATUS                 status;
82     RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
83     HANDLE                   deviceParameterHandle = NULL;
84     HANDLE                   deviceSubkeyHandle = NULL;
85     ULONG                    defaultParameterValue;
86 
87     PAGED_CODE();
88 
89     //
90     // open the given parameter
91     //
92 
93     status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
94                                      PLUGPLAY_REGKEY_DEVICE,
95                                      KEY_READ,
96                                      &deviceParameterHandle);
97 
98     if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
99 
100         UNICODE_STRING subkeyName;
101         OBJECT_ATTRIBUTES objectAttributes = {0};
102 
103         RtlInitUnicodeString(&subkeyName, SubkeyName);
104         InitializeObjectAttributes(&objectAttributes,
105                                    &subkeyName,
106                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
107                                    deviceParameterHandle,
108                                    NULL);
109 
110         status = ZwOpenKey(&deviceSubkeyHandle,
111                            KEY_READ,
112                            &objectAttributes);
113         if (!NT_SUCCESS(status)) {
114             ZwClose(deviceParameterHandle);
115         }
116 
117     }
118 
119     if (NT_SUCCESS(status)) {
120 
121         defaultParameterValue = *ParameterValue;
122 
123         queryTable->Flags         = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK;
124         queryTable->Name          = ParameterName;
125         queryTable->EntryContext  = ParameterValue;
126         queryTable->DefaultType   = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
127         queryTable->DefaultData   = NULL;
128         queryTable->DefaultLength = 0;
129 
130         status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
131                                         (PWSTR)(SubkeyName ?
132                                                 deviceSubkeyHandle :
133                                                 deviceParameterHandle),
134                                         queryTable,
135                                         NULL,
136                                         NULL);
137         if (!NT_SUCCESS(status)) {
138             *ParameterValue = defaultParameterValue; // use default value
139         }
140 
141         //
142         // close what we open
143         //
144 
145         if (SubkeyName) {
146             ZwClose(deviceSubkeyHandle);
147         }
148 
149         ZwClose(deviceParameterHandle);
150     }
151 
152     if (!NT_SUCCESS(status)) {
153 
154         //
155         // Windows 2000 SP3 uses the driver-specific key, so look in there
156         //
157 
158         status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
159                                          PLUGPLAY_REGKEY_DRIVER,
160                                          KEY_READ,
161                                          &deviceParameterHandle);
162 
163         if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
164 
165             UNICODE_STRING subkeyName;
166             OBJECT_ATTRIBUTES objectAttributes = {0};
167 
168             RtlInitUnicodeString(&subkeyName, SubkeyName);
169             InitializeObjectAttributes(&objectAttributes,
170                                        &subkeyName,
171                                        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
172                                        deviceParameterHandle,
173                                        NULL);
174 
175             status = ZwOpenKey(&deviceSubkeyHandle, KEY_READ, &objectAttributes);
176 
177             if (!NT_SUCCESS(status)) {
178                 ZwClose(deviceParameterHandle);
179             }
180         }
181 
182         if (NT_SUCCESS(status)) {
183 
184             defaultParameterValue = *ParameterValue;
185 
186             queryTable->Flags         = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK;
187             queryTable->Name          = ParameterName;
188             queryTable->EntryContext  = ParameterValue;
189             queryTable->DefaultType   =  (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
190             queryTable->DefaultData   = NULL;
191             queryTable->DefaultLength = 0;
192 
193             status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
194                                             (PWSTR)(SubkeyName ?
195                                                     deviceSubkeyHandle :
196                                                     deviceParameterHandle),
197                                             queryTable,
198                                             NULL,
199                                             NULL);
200             if (NT_SUCCESS(status)) {
201 
202                 //
203                 // Migrate the value over to the device-specific key
204                 //
205 
206                 ClassSetDeviceParameter(FdoExtension, SubkeyName, ParameterName, *ParameterValue);
207 
208             } else {
209 
210                 //
211                 // Use the default value
212                 //
213 
214                 *ParameterValue = defaultParameterValue;
215             }
216 
217             if (SubkeyName) {
218                 ZwClose(deviceSubkeyHandle);
219             }
220 
221             ZwClose(deviceParameterHandle);
222         }
223     }
224 
225     return;
226 
227 } // end ClassGetDeviceParameter()
228 
_IRQL_requires_max_(PASSIVE_LEVEL)229 _IRQL_requires_max_(PASSIVE_LEVEL)
230 NTSTATUS
231 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
232 ClassSetDeviceParameter(
233     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
234     _In_opt_ PWSTR SubkeyName,
235     _In_ PWSTR ParameterName,
236     _In_ ULONG ParameterValue)
237 {
238     NTSTATUS                 status;
239     HANDLE                   deviceParameterHandle = NULL;
240     HANDLE                   deviceSubkeyHandle = NULL;
241 
242     PAGED_CODE();
243 
244     //
245     // open the given parameter
246     //
247 
248     status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
249                                      PLUGPLAY_REGKEY_DEVICE,
250                                      KEY_READ | KEY_WRITE,
251                                      &deviceParameterHandle);
252 
253     if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
254 
255         UNICODE_STRING subkeyName;
256         OBJECT_ATTRIBUTES objectAttributes;
257 
258         RtlInitUnicodeString(&subkeyName, SubkeyName);
259         InitializeObjectAttributes(&objectAttributes,
260                                    &subkeyName,
261                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
262                                    deviceParameterHandle,
263                                    NULL);
264 
265         status = ZwCreateKey(&deviceSubkeyHandle,
266                              KEY_READ | KEY_WRITE,
267                              &objectAttributes,
268                              0, NULL, 0, NULL);
269         if (!NT_SUCCESS(status)) {
270             ZwClose(deviceParameterHandle);
271         }
272 
273     }
274 
275     if (NT_SUCCESS(status)) {
276 
277         status = RtlWriteRegistryValue(
278             RTL_REGISTRY_HANDLE,
279             (PWSTR) (SubkeyName ?
280                      deviceSubkeyHandle :
281                      deviceParameterHandle),
282             ParameterName,
283             REG_DWORD,
284             &ParameterValue,
285             sizeof(ULONG));
286 
287         //
288         // close what we open
289         //
290 
291         if (SubkeyName) {
292             ZwClose(deviceSubkeyHandle);
293         }
294 
295         ZwClose(deviceParameterHandle);
296     }
297 
298     return status;
299 
300 } // end ClassSetDeviceParameter()
301 
302 
303 /*
304  *  ClassScanForSpecial
305  *
306  *      This routine was written to simplify scanning for special
307  *      hardware based upon id strings.  it does not check the registry.
308  */
309 
_IRQL_requires_max_(PASSIVE_LEVEL)310 _IRQL_requires_max_(PASSIVE_LEVEL)
311 VOID
312 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
313 ClassScanForSpecial(
314     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
315     _In_ CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[],
316     _In_ PCLASS_SCAN_FOR_SPECIAL_HANDLER Function)
317 {
318     PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
319     PUCHAR vendorId;
320     PUCHAR productId;
321     PUCHAR productRevision;
322     UCHAR nullString[] = "";
323 
324     PAGED_CODE();
325     NT_ASSERT(DeviceList);
326     NT_ASSERT(Function);
327 
328     deviceDescriptor = FdoExtension->DeviceDescriptor;
329 
330     if (DeviceList == NULL) {
331         return;
332     }
333     if (Function == NULL) {
334         return;
335     }
336 
337     //
338     // SCSI sets offsets to -1, ATAPI sets to 0.  check for both.
339     //
340 
341     if (deviceDescriptor->VendorIdOffset != 0 &&
342         deviceDescriptor->VendorIdOffset != -1) {
343         vendorId = ((PUCHAR)deviceDescriptor);
344         vendorId += deviceDescriptor->VendorIdOffset;
345     } else {
346         vendorId = nullString;
347     }
348     if (deviceDescriptor->ProductIdOffset != 0 &&
349         deviceDescriptor->ProductIdOffset != -1) {
350         productId = ((PUCHAR)deviceDescriptor);
351         productId += deviceDescriptor->ProductIdOffset;
352     } else {
353         productId = nullString;
354     }
355     if (deviceDescriptor->ProductRevisionOffset != 0 &&
356         deviceDescriptor->ProductRevisionOffset != -1) {
357         productRevision = ((PUCHAR)deviceDescriptor);
358         productRevision += deviceDescriptor->ProductRevisionOffset;
359     } else {
360         productRevision = nullString;
361     }
362 
363     //
364     // loop while the device list is valid (not null-filled)
365     //
366 
367     for (;(DeviceList->VendorId        != NULL ||
368            DeviceList->ProductId       != NULL ||
369            DeviceList->ProductRevision != NULL);DeviceList++) {
370 
371         if (ClasspMyStringMatches(DeviceList->VendorId,        (PCHAR)vendorId) &&
372             ClasspMyStringMatches(DeviceList->ProductId,       (PCHAR)productId) &&
373             ClasspMyStringMatches(DeviceList->ProductRevision, (PCHAR)productRevision)
374             ) {
375 
376             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspScanForSpecialByInquiry: Found matching "
377                         "controller Ven: %s Prod: %s Rev: %s\n",
378                         (PCSZ)vendorId, (PCSZ)productId, (PCSZ)productRevision));
379 
380             //
381             // pass the context to the call back routine and exit
382             //
383 
384             (Function)(FdoExtension, DeviceList->Data);
385 
386             //
387             // for CHK builds, try to prevent wierd stacks by having a debug
388             // print here. it's a hack, but i know of no other way to prevent
389             // the stack from being wrong.
390             //
391 
392             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspScanForSpecialByInquiry: "
393                         "completed callback\n"));
394             return;
395 
396         } // else the strings did not match
397 
398     } // none of the devices matched.
399 
400     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspScanForSpecialByInquiry: no match found for %p\n",
401                 FdoExtension->DeviceObject));
402     return;
403 
404 } // end ClasspScanForSpecialByInquiry()
405 
406 
407 //
408 // In order to provide better performance without the need to reboot,
409 // we need to implement a self-adjusting method to set and clear the
410 // srb flags based upon current performance.
411 //
412 // whenever there is an error, immediately grab the spin lock.  the
413 // MP perf hit here is acceptable, since we're in an error path.  this
414 // is also neccessary because we are guaranteed to be modifying the
415 // SRB flags here, setting SuccessfulIO to zero, and incrementing the
416 // actual error count (which is always done within this spinlock).
417 //
418 // whenever there is no error, increment a counter.  if there have been
419 // errors on the device, and we've enabled dynamic perf, *and* we've
420 // just crossed the perf threshhold, then grab the spin lock and
421 // double check that the threshhold has, indeed been hit(*). then
422 // decrement the error count, and if it's dropped sufficiently, undo
423 // some of the safety changes made in the SRB flags due to the errors.
424 //
425 // * this works in all cases.  even if lots of ios occur after the
426 //   previous guy went in and cleared the successfulio counter, that
427 //   just means that we've hit the threshhold again, and so it's proper
428 //   to run the inner loop again.
429 //
430 
431 VOID
ClasspPerfIncrementErrorCount(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)432 ClasspPerfIncrementErrorCount(
433     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
434     )
435 {
436     PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
437     KIRQL oldIrql;
438     ULONG errors;
439 
440     KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
441 
442     fdoData->Perf.SuccessfulIO = 0; // implicit interlock
443     errors = InterlockedIncrement((volatile LONG *)&FdoExtension->ErrorCount);
444 
445     if (!fdoData->DisableThrottling) {
446 
447         if (errors >= CLASS_ERROR_LEVEL_1) {
448 
449             //
450             // If the error count has exceeded the error limit, then disable
451             // any tagged queuing, multiple requests per lu queueing
452             // and sychronous data transfers.
453             //
454             // Clearing the no queue freeze flag prevents the port driver
455             // from sending multiple requests per logical unit.
456             //
457 
458             CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
459             CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
460 
461             SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
462 
463             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: "
464                         "Too many errors; disabling tagged queuing and "
465                         "synchronous data tranfers.\n"));
466 
467         }
468 
469         if (errors >= CLASS_ERROR_LEVEL_2) {
470 
471             //
472             // If a second threshold is reached, disable disconnects.
473             //
474 
475             SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
476             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: "
477                         "Too many errors; disabling disconnects.\n"));
478         }
479     }
480 
481     KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
482     return;
483 }
484 
485 VOID
ClasspPerfIncrementSuccessfulIo(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)486 ClasspPerfIncrementSuccessfulIo(
487     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
488     )
489 {
490     PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
491     KIRQL oldIrql;
492     ULONG errors;
493     ULONG succeeded = 0;
494 
495     //
496     // don't take a hit from the interlocked op unless we're in
497     // a degraded state and we've got a threshold to hit.
498     //
499 
500     if (FdoExtension->ErrorCount == 0) {
501         return;
502     }
503 
504     if (fdoData->Perf.ReEnableThreshhold == 0) {
505         return;
506     }
507 
508     succeeded = InterlockedIncrement((volatile LONG *)&fdoData->Perf.SuccessfulIO);
509     if (succeeded < fdoData->Perf.ReEnableThreshhold) {
510         return;
511     }
512 
513     //
514     // if we hit the threshold, grab the spinlock and verify we've
515     // actually done so.  this allows us to ignore the spinlock 99%
516     // of the time.
517     //
518 
519     KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
520 
521     //
522     // re-read the value, so we don't run this multiple times
523     // for a single threshhold being hit.  this keeps errorcount
524     // somewhat useful.
525     //
526 
527     succeeded = fdoData->Perf.SuccessfulIO;
528 
529     if ((FdoExtension->ErrorCount != 0) &&
530         (fdoData->Perf.ReEnableThreshhold <= succeeded)
531         ) {
532 
533         fdoData->Perf.SuccessfulIO = 0; // implicit interlock
534 
535         NT_ASSERT(FdoExtension->ErrorCount > 0);
536         errors = InterlockedDecrement((volatile LONG *)&FdoExtension->ErrorCount);
537 
538         //
539         // note: do in reverse order of the sets "just in case"
540         //
541 
542         if (errors < CLASS_ERROR_LEVEL_2) {
543             if (errors == CLASS_ERROR_LEVEL_2 - 1) {
544                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementSuccessfulIo: "
545                             "Error level 2 no longer required.\n"));
546             }
547             if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
548                            SRB_FLAGS_DISABLE_DISCONNECT)) {
549                 CLEAR_FLAG(FdoExtension->SrbFlags,
550                            SRB_FLAGS_DISABLE_DISCONNECT);
551             }
552         }
553 
554         if (errors < CLASS_ERROR_LEVEL_1) {
555             if (errors == CLASS_ERROR_LEVEL_1 - 1) {
556                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementSuccessfulIo: "
557                             "Error level 1 no longer required.\n"));
558             }
559             if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
560                            SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
561                 CLEAR_FLAG(FdoExtension->SrbFlags,
562                            SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
563             }
564             if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
565                           SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
566                 SET_FLAG(FdoExtension->SrbFlags,
567                          SRB_FLAGS_QUEUE_ACTION_ENABLE);
568             }
569             if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
570                           SRB_FLAGS_NO_QUEUE_FREEZE)) {
571                 SET_FLAG(FdoExtension->SrbFlags,
572                          SRB_FLAGS_NO_QUEUE_FREEZE);
573             }
574         }
575     } // end of threshhold definitely being hit for first time
576 
577     KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
578     return;
579 }
580 
581 
ClasspBuildDeviceMdl(PVOID Buffer,ULONG BufferLen,BOOLEAN WriteToDevice)582 PMDL ClasspBuildDeviceMdl(PVOID Buffer, ULONG BufferLen, BOOLEAN WriteToDevice)
583 {
584     PMDL mdl;
585 
586     mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL);
587     if (mdl){
588         _SEH2_TRY {
589             MmProbeAndLockPages(mdl, KernelMode, WriteToDevice ? IoReadAccess : IoWriteAccess);
590 #ifdef _MSC_VER
591         #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
592 #endif
593         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
594             NTSTATUS status = _SEH2_GetExceptionCode();
595 
596             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspBuildDeviceMdl: MmProbeAndLockPages failed with %xh.", status));
597             IoFreeMdl(mdl);
598             mdl = NULL;
599         } _SEH2_END;
600     }
601     else {
602         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspBuildDeviceMdl: IoAllocateMdl failed"));
603     }
604 
605     return mdl;
606 }
607 
608 
BuildDeviceInputMdl(PVOID Buffer,ULONG BufferLen)609 PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen)
610 {
611     return ClasspBuildDeviceMdl(Buffer, BufferLen, FALSE);
612 }
613 
614 
ClasspFreeDeviceMdl(PMDL Mdl)615 VOID ClasspFreeDeviceMdl(PMDL Mdl)
616 {
617     MmUnlockPages(Mdl);
618     IoFreeMdl(Mdl);
619 }
620 
621 
FreeDeviceInputMdl(PMDL Mdl)622 VOID FreeDeviceInputMdl(PMDL Mdl)
623 {
624     ClasspFreeDeviceMdl(Mdl);
625     return;
626 }
627 
628 
629 #if 0
630     VOID
631     ClasspPerfResetCounters(
632         IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
633         )
634     {
635         PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
636         KIRQL oldIrql;
637 
638         KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
639         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfResetCounters: "
640                     "Resetting all perf counters.\n"));
641         fdoData->Perf.SuccessfulIO = 0;
642         FdoExtension->ErrorCount = 0;
643 
644         if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
645                        SRB_FLAGS_DISABLE_DISCONNECT)) {
646             CLEAR_FLAG(FdoExtension->SrbFlags,
647                        SRB_FLAGS_DISABLE_DISCONNECT);
648         }
649         if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
650                        SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
651             CLEAR_FLAG(FdoExtension->SrbFlags,
652                        SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
653         }
654         if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
655                       SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
656             SET_FLAG(FdoExtension->SrbFlags,
657                      SRB_FLAGS_QUEUE_ACTION_ENABLE);
658         }
659         if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
660                       SRB_FLAGS_NO_QUEUE_FREEZE)) {
661             SET_FLAG(FdoExtension->SrbFlags,
662                      SRB_FLAGS_NO_QUEUE_FREEZE);
663         }
664         KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
665         return;
666     }
667 #endif
668 
669 
670 /*++
671 
672 ClasspDuidGetDeviceIdProperty
673 
674 Routine Description:
675 
676     Add StorageDeviceIdProperty to the device unique ID structure.
677 
678 Arguments:
679 
680     DeviceObject - a pointer to the device object
681     Irp - a pointer to the I/O request packet
682 
683 Return Value:
684 
685     Status Code
686 
687 --*/
688 NTSTATUS
ClasspDuidGetDeviceIdProperty(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)689 ClasspDuidGetDeviceIdProperty(
690     IN PDEVICE_OBJECT DeviceObject,
691     IN PIRP Irp
692     )
693 {
694     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
695     PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL;
696     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
697     PSTORAGE_DESCRIPTOR_HEADER descHeader;
698     PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
699     PUCHAR dest;
700 
701     STORAGE_PROPERTY_ID propertyId = StorageDeviceIdProperty;
702 
703     NTSTATUS status;
704 
705     ULONG queryLength;
706     ULONG offset;
707 
708     //
709     // Get the VPD page 83h data.
710     //
711 
712     status = ClassGetDescriptor(commonExtension->LowerDeviceObject,
713                                 &propertyId,
714                                 (PVOID *)&deviceIdDescriptor);
715 
716     if (!NT_SUCCESS(status) || !deviceIdDescriptor) {
717         goto FnExit;
718     }
719 
720     queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
721     descHeader = Irp->AssociatedIrp.SystemBuffer;
722 
723     //
724     // Adjust required size and potential destination location.
725     //
726 
727     offset = descHeader->Size;
728     dest = (PUCHAR)descHeader + offset;
729 
730     descHeader->Size += deviceIdDescriptor->Size;
731 
732     if (queryLength < descHeader->Size) {
733 
734         //
735         // Output buffer is too small.  Return error and make sure
736         // the caller gets info about required buffer size.
737         //
738 
739         Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
740         status = STATUS_BUFFER_OVERFLOW;
741         goto FnExit;
742     }
743 
744     storageDuid = Irp->AssociatedIrp.SystemBuffer;
745     storageDuid->StorageDeviceIdOffset = offset;
746 
747     RtlCopyMemory(dest,
748                   deviceIdDescriptor,
749                   deviceIdDescriptor->Size);
750 
751     Irp->IoStatus.Information = storageDuid->Size;
752     status = STATUS_SUCCESS;
753 
754 FnExit:
755 
756     FREE_POOL(deviceIdDescriptor);
757 
758     return status;
759 }
760 
761 
762 
763 /*++
764 
765 ClasspDuidGetDeviceProperty
766 
767 Routine Description:
768 
769     Add StorageDeviceProperty to the device unique ID structure.
770 
771 Arguments:
772 
773     DeviceObject - a pointer to the device object
774     Irp - a pointer to the I/O request packet
775 
776 Return Value:
777 
778     Status Code
779 
780 --*/
781 NTSTATUS
ClasspDuidGetDeviceProperty(PDEVICE_OBJECT DeviceObject,PIRP Irp)782 ClasspDuidGetDeviceProperty(
783     PDEVICE_OBJECT DeviceObject,
784     PIRP Irp
785     )
786 {
787     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
788     PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = fdoExtension->DeviceDescriptor;
789     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
790     PSTORAGE_DESCRIPTOR_HEADER descHeader;
791     PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
792     PUCHAR dest;
793 
794     NTSTATUS status = STATUS_NOT_FOUND;
795 
796     ULONG queryLength;
797     ULONG offset;
798 
799     //
800     // Use the StorageDeviceProperty already cached in the device extension.
801     //
802 
803     if (!deviceDescriptor) {
804         goto FnExit;
805     }
806 
807     queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
808     descHeader = Irp->AssociatedIrp.SystemBuffer;
809 
810     //
811     // Use this info only if serial number is available.
812     //
813 
814     if (deviceDescriptor->SerialNumberOffset == 0) {
815         goto FnExit;
816     }
817 
818     //
819     // Adjust required size and potential destination location.
820     //
821 
822     offset = descHeader->Size;
823     dest = (PUCHAR)descHeader + offset;
824 
825     descHeader->Size += deviceDescriptor->Size;
826 
827     if (queryLength < descHeader->Size) {
828 
829         //
830         // Output buffer is too small.  Return error and make sure
831         // the caller get info about required buffer size.
832         //
833 
834         Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
835         status = STATUS_BUFFER_OVERFLOW;
836         goto FnExit;
837     }
838 
839     storageDuid = Irp->AssociatedIrp.SystemBuffer;
840     storageDuid->StorageDeviceOffset = offset;
841 
842     RtlCopyMemory(dest,
843                   deviceDescriptor,
844                   deviceDescriptor->Size);
845 
846     Irp->IoStatus.Information = storageDuid->Size;
847     status = STATUS_SUCCESS;
848 
849 FnExit:
850 
851     return status;
852 }
853 
854 
855 /*++
856 
857 ClasspDuidGetDriveLayout
858 
859 Routine Description:
860 
861     Add drive layout signature to the device unique ID structure.
862     Layout signature is only added for disk-type devices.
863 
864 Arguments:
865 
866     DeviceObject - a pointer to the device object
867     Irp - a pointer to the I/O request packet
868 
869 Return Value:
870 
871     Status Code
872 
873 --*/
874 NTSTATUS
ClasspDuidGetDriveLayout(PDEVICE_OBJECT DeviceObject,PIRP Irp)875 ClasspDuidGetDriveLayout(
876     PDEVICE_OBJECT DeviceObject,
877     PIRP Irp
878     )
879 {
880     PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
881     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
882     PSTORAGE_DESCRIPTOR_HEADER descHeader;
883     PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
884     PSTORAGE_DEVICE_LAYOUT_SIGNATURE driveLayoutSignature;
885 
886     NTSTATUS status = STATUS_NOT_FOUND;
887 
888     ULONG queryLength;
889     ULONG offset;
890 
891     //
892     // Only process disk-type devices.
893     //
894 
895     if (DeviceObject->DeviceType != FILE_DEVICE_DISK) {
896         goto FnExit;
897     }
898 
899     //
900     // Get current partition table and process only if GPT
901     // or MBR layout.
902     //
903 
904     status = IoReadPartitionTableEx(DeviceObject, &layoutEx);
905 
906     if (!NT_SUCCESS(status)) {
907         status = STATUS_NOT_FOUND;
908         goto FnExit;
909     }
910 
911     if (layoutEx->PartitionStyle != PARTITION_STYLE_GPT &&
912         layoutEx->PartitionStyle != PARTITION_STYLE_MBR) {
913         status = STATUS_NOT_FOUND;
914         goto FnExit;
915     }
916 
917     queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
918     descHeader = Irp->AssociatedIrp.SystemBuffer;
919 
920     //
921     // Adjust required size and potential destination location.
922     //
923 
924     offset = descHeader->Size;
925     driveLayoutSignature = (PSTORAGE_DEVICE_LAYOUT_SIGNATURE)((PUCHAR)descHeader + offset);
926 
927     descHeader->Size += sizeof(STORAGE_DEVICE_LAYOUT_SIGNATURE);
928 
929     if (queryLength < descHeader->Size) {
930 
931         //
932         // Output buffer is too small.  Return error and make sure
933         // the caller get info about required buffer size.
934         //
935 
936         Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
937         status = STATUS_BUFFER_OVERFLOW;
938         goto FnExit;
939     }
940 
941     storageDuid = Irp->AssociatedIrp.SystemBuffer;
942 
943     driveLayoutSignature->Size = sizeof(STORAGE_DEVICE_LAYOUT_SIGNATURE);
944     driveLayoutSignature->Version = DUID_VERSION_1;
945 
946     if (layoutEx->PartitionStyle == PARTITION_STYLE_MBR) {
947 
948         driveLayoutSignature->Mbr = TRUE;
949 
950         RtlCopyMemory(&driveLayoutSignature->DeviceSpecific.MbrSignature,
951                       &layoutEx->Mbr.Signature,
952                       sizeof(layoutEx->Mbr.Signature));
953 
954     } else {
955 
956         driveLayoutSignature->Mbr = FALSE;
957 
958         RtlCopyMemory(&driveLayoutSignature->DeviceSpecific.GptDiskId,
959                       &layoutEx->Gpt.DiskId,
960                       sizeof(layoutEx->Gpt.DiskId));
961     }
962 
963     storageDuid->DriveLayoutSignatureOffset = offset;
964 
965     Irp->IoStatus.Information = storageDuid->Size;
966     status = STATUS_SUCCESS;
967 
968 
969 FnExit:
970 
971     FREE_POOL(layoutEx);
972 
973     return status;
974 }
975 
976 
977 /*++
978 
979 ClasspDuidQueryProperty
980 
981 Routine Description:
982 
983     Handles IOCTL_STORAGE_QUERY_PROPERTY for device unique ID requests
984     (when PropertyId is StorageDeviceUniqueIdProperty).
985 
986 Arguments:
987 
988     DeviceObject - a pointer to the device object
989     Irp - a pointer to the I/O request packet
990 
991 Return Value:
992 
993     Status Code
994 
995 --*/
996 NTSTATUS
ClasspDuidQueryProperty(PDEVICE_OBJECT DeviceObject,PIRP Irp)997 ClasspDuidQueryProperty(
998     PDEVICE_OBJECT DeviceObject,
999     PIRP Irp
1000     )
1001 {
1002     PSTORAGE_PROPERTY_QUERY query =  Irp->AssociatedIrp.SystemBuffer;
1003     PSTORAGE_DESCRIPTOR_HEADER descHeader;
1004     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
1005 
1006     NTSTATUS status;
1007 
1008     ULONG outLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
1009 
1010     BOOLEAN includeOptionalIds;
1011     BOOLEAN overflow = FALSE;
1012     BOOLEAN infoFound = FALSE;
1013     BOOLEAN useStatus = TRUE;   // Use the status directly instead of relying on overflow and infoFound flags.
1014 
1015     //
1016     // Must run at less then dispatch.
1017     //
1018 
1019     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
1020 
1021         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
1022         status = STATUS_INVALID_LEVEL;
1023         goto FnExit;
1024     }
1025 
1026     //
1027     // Check proper query type.
1028     //
1029 
1030     if (query->QueryType == PropertyExistsQuery) {
1031         Irp->IoStatus.Information = 0;
1032         status = STATUS_SUCCESS;
1033         goto FnExit;
1034     }
1035 
1036     if (query->QueryType != PropertyStandardQuery) {
1037         status = STATUS_NOT_SUPPORTED;
1038         goto FnExit;
1039     }
1040 
1041     //
1042     // Check AdditionalParameters validity.
1043     //
1044 
1045     if (query->AdditionalParameters[0] == DUID_INCLUDE_SOFTWARE_IDS) {
1046         includeOptionalIds = TRUE;
1047     } else if (query->AdditionalParameters[0] == DUID_HARDWARE_IDS_ONLY) {
1048         includeOptionalIds = FALSE;
1049     } else {
1050         status = STATUS_INVALID_PARAMETER;
1051         goto FnExit;
1052     }
1053 
1054     //
1055     // Verify output parameters.
1056     //
1057 
1058     if (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
1059 
1060         status = STATUS_INFO_LENGTH_MISMATCH;
1061         goto FnExit;
1062     }
1063 
1064     //
1065     // From this point forward the status depends on the overflow
1066     // and infoFound flags.
1067     //
1068 
1069     useStatus = FALSE;
1070 
1071     descHeader = Irp->AssociatedIrp.SystemBuffer;
1072     RtlZeroMemory(descHeader, outLength);
1073 
1074     descHeader->Version = DUID_VERSION_1;
1075     descHeader->Size = sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER);
1076 
1077     //
1078     // Try to build device unique id from StorageDeviceIdProperty.
1079     //
1080 
1081     status = ClasspDuidGetDeviceIdProperty(DeviceObject,
1082                                            Irp);
1083 
1084     if (status == STATUS_BUFFER_OVERFLOW) {
1085         overflow = TRUE;
1086     }
1087 
1088     if (NT_SUCCESS(status)) {
1089         infoFound = TRUE;
1090     }
1091 
1092     //
1093     // Try to build device unique id from StorageDeviceProperty.
1094     //
1095 
1096     status = ClasspDuidGetDeviceProperty(DeviceObject,
1097                                          Irp);
1098 
1099     if (status == STATUS_BUFFER_OVERFLOW) {
1100         overflow = TRUE;
1101     }
1102 
1103     if (NT_SUCCESS(status)) {
1104         infoFound = TRUE;
1105     }
1106 
1107     //
1108     // The following portion is optional and only included if the
1109     // caller requested software IDs.
1110     //
1111 
1112     if (!includeOptionalIds) {
1113         goto FnExit;
1114     }
1115 
1116     //
1117     // Try to build device unique id from drive layout signature (disk
1118     // devices only).
1119     //
1120 
1121     status = ClasspDuidGetDriveLayout(DeviceObject,
1122                                       Irp);
1123 
1124     if (status == STATUS_BUFFER_OVERFLOW) {
1125         overflow = TRUE;
1126     }
1127 
1128     if (NT_SUCCESS(status)) {
1129         infoFound = TRUE;
1130     }
1131 
1132 FnExit:
1133 
1134     if (!useStatus) {
1135 
1136         //
1137         // Return overflow, success, or a generic error.
1138         //
1139 
1140         if (overflow) {
1141 
1142             //
1143             // If output buffer is STORAGE_DESCRIPTOR_HEADER, then return
1144             // success to the user.  Otherwise, send an error so the user
1145             // knows a larger buffer is required.
1146             //
1147 
1148             if (outLength == sizeof(STORAGE_DESCRIPTOR_HEADER)) {
1149                 status = STATUS_SUCCESS;
1150                 Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
1151             } else {
1152                 status = STATUS_BUFFER_OVERFLOW;
1153             }
1154 
1155         } else if (infoFound) {
1156             status = STATUS_SUCCESS;
1157 
1158             //
1159             // Exercise the compare routine.  This should always succeed.
1160             //
1161 
1162             NT_ASSERT(DuidExactMatch == CompareStorageDuids(Irp->AssociatedIrp.SystemBuffer,
1163                                                          Irp->AssociatedIrp.SystemBuffer));
1164 
1165         } else {
1166             status = STATUS_NOT_FOUND;
1167         }
1168     }
1169 
1170     Irp->IoStatus.Status = status;
1171 
1172     ClassReleaseRemoveLock(DeviceObject, Irp);
1173     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1174 
1175     return status;
1176 }
1177 
1178 /*++////////////////////////////////////////////////////////////////////////////
1179 
1180 ClasspWriteCacheProperty()
1181 
1182 Routine Description:
1183 
1184     This routine reads the caching mode page from the device to
1185     build the Write Cache property page.
1186 
1187 Arguments:
1188 
1189     DeviceObject - The device object to handle this irp
1190 
1191     Irp - The IRP for this request
1192 
1193     Srb - SRB allocated by the dispatch routine
1194 
1195 Return Value:
1196 
1197 --*/
1198 
ClasspWriteCacheProperty(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)1199 NTSTATUS ClasspWriteCacheProperty(
1200     _In_ PDEVICE_OBJECT DeviceObject,
1201     _In_ PIRP Irp,
1202     _Inout_ PSCSI_REQUEST_BLOCK Srb
1203     )
1204 {
1205     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1206     PSTORAGE_WRITE_CACHE_PROPERTY writeCache;
1207     PSTORAGE_PROPERTY_QUERY query =  Irp->AssociatedIrp.SystemBuffer;
1208     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
1209     PMODE_PARAMETER_HEADER modeData = NULL;
1210     PMODE_CACHING_PAGE pageData = NULL;
1211     ULONG length, information = 0;
1212     NTSTATUS status;
1213     PCDB cdb;
1214 
1215     //
1216     // Must run at less then dispatch.
1217     //
1218 
1219     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
1220 
1221         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
1222         status = STATUS_INVALID_LEVEL;
1223         goto WriteCacheExit;
1224     }
1225 
1226     //
1227     // Check proper query type.
1228     //
1229 
1230     if (query->QueryType == PropertyExistsQuery) {
1231         status = STATUS_SUCCESS;
1232         goto WriteCacheExit;
1233     }
1234 
1235     if (query->QueryType != PropertyStandardQuery) {
1236         status = STATUS_NOT_SUPPORTED;
1237         goto WriteCacheExit;
1238     }
1239 
1240     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
1241 
1242     if (length < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
1243         status = STATUS_INFO_LENGTH_MISMATCH;
1244         goto WriteCacheExit;
1245     }
1246 
1247     writeCache = (PSTORAGE_WRITE_CACHE_PROPERTY) Irp->AssociatedIrp.SystemBuffer;
1248     RtlZeroMemory(writeCache, length);
1249 
1250     //
1251     // Set version and required size.
1252     //
1253 
1254     writeCache->Version = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
1255     writeCache->Size = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
1256 
1257     if (length < sizeof(STORAGE_WRITE_CACHE_PROPERTY)) {
1258         information = sizeof(STORAGE_DESCRIPTOR_HEADER);
1259         status = STATUS_SUCCESS;
1260         goto WriteCacheExit;
1261     }
1262 
1263     //
1264     // Set known values
1265     //
1266 
1267     writeCache->NVCacheEnabled = FALSE;
1268     writeCache->UserDefinedPowerProtection = TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
1269 
1270     //
1271     // Check for flush cache support by sending a sync cache command
1272     // to the device.
1273     //
1274 
1275     //
1276     // Set timeout value and mark the request as not being a tagged request.
1277     //
1278     SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue * 4);
1279     SrbSetRequestTag(Srb, SP_UNTAGGED);
1280     SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
1281     SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
1282 
1283     SrbSetCdbLength(Srb, 10);
1284     cdb = SrbGetCdb(Srb);
1285     cdb->CDB10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
1286 
1287     status = ClassSendSrbSynchronous(DeviceObject,
1288                                      Srb,
1289                                      NULL,
1290                                      0,
1291                                      TRUE);
1292     if (NT_SUCCESS(status)) {
1293         writeCache->FlushCacheSupported = TRUE;
1294     } else {
1295         //
1296         // Device does not support sync cache
1297         //
1298 
1299         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Synchronize cache failed with status 0x%X\n", status));
1300         writeCache->FlushCacheSupported = FALSE;
1301         //
1302         // Reset the status if there was any failure
1303         //
1304         status = STATUS_SUCCESS;
1305     }
1306 
1307     modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
1308                                      MODE_PAGE_DATA_SIZE,
1309                                      CLASS_TAG_MODE_DATA);
1310 
1311     if (modeData == NULL) {
1312         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to allocate mode data buffer\n"));
1313         status = STATUS_INSUFFICIENT_RESOURCES;
1314         goto WriteCacheExit;
1315     }
1316 
1317     RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
1318 
1319     length = ClassModeSense(DeviceObject,
1320                             (PCHAR) modeData,
1321                             MODE_PAGE_DATA_SIZE,
1322                             MODE_PAGE_CACHING);
1323 
1324     if (length < sizeof(MODE_PARAMETER_HEADER)) {
1325 
1326         //
1327         // Retry the request in case of a check condition.
1328         //
1329 
1330         length = ClassModeSense(DeviceObject,
1331                                 (PCHAR) modeData,
1332                                 MODE_PAGE_DATA_SIZE,
1333                                 MODE_PAGE_CACHING);
1334 
1335         if (length < sizeof(MODE_PARAMETER_HEADER)) {
1336             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Mode Sense failed\n"));
1337             status = STATUS_IO_DEVICE_ERROR;
1338             goto WriteCacheExit;
1339         }
1340     }
1341 
1342     //
1343     // If the length is greater than length indicated by the mode data reset
1344     // the data to the mode data.
1345     //
1346 
1347     if (length > (ULONG) (modeData->ModeDataLength + 1)) {
1348         length = modeData->ModeDataLength + 1;
1349     }
1350 
1351     //
1352     // Look for caching page in the returned mode page data.
1353     //
1354 
1355     pageData = ClassFindModePage((PCHAR) modeData,
1356                                  length,
1357                                  MODE_PAGE_CACHING,
1358                                  TRUE);
1359 
1360     //
1361     // Check if valid caching page exists.
1362     //
1363 
1364     if (pageData == NULL) {
1365 
1366         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to find caching mode page.\n"));
1367         //
1368         // Set write cache value as unknown.
1369         //
1370         writeCache->WriteCacheEnabled = WriteCacheEnableUnknown;
1371         writeCache->WriteCacheType = WriteCacheTypeUnknown;
1372     } else {
1373         writeCache->WriteCacheEnabled = pageData->WriteCacheEnable ?
1374                                             WriteCacheEnabled : WriteCacheDisabled;
1375 
1376         writeCache->WriteCacheType = pageData->WriteCacheEnable ?
1377                                             WriteCacheTypeWriteBack : WriteCacheTypeUnknown;
1378     }
1379 
1380     //
1381     // Check write through support. If the device previously failed a write request
1382     // with FUA bit is set, then CLASS_SPECIAL_FUA_NOT_SUPPORTED will be set,
1383     // which means write through is not support by the device.
1384     //
1385 
1386     if ((modeData->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) &&
1387         (!TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED))) {
1388         writeCache->WriteThroughSupported = WriteThroughSupported;
1389     } else {
1390         writeCache->WriteThroughSupported = WriteThroughNotSupported;
1391     }
1392 
1393     //
1394     // Get the changeable caching mode page and check write cache is changeable.
1395     //
1396 
1397     RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
1398 
1399     length = ClasspModeSense(DeviceObject,
1400                             (PCHAR) modeData,
1401                             MODE_PAGE_DATA_SIZE,
1402                             MODE_PAGE_CACHING,
1403                             MODE_SENSE_CHANGEABLE_VALUES);
1404 
1405     if (length < sizeof(MODE_PARAMETER_HEADER)) {
1406 
1407         //
1408         // Retry the request in case of a check condition.
1409         //
1410 
1411         length = ClasspModeSense(DeviceObject,
1412                                 (PCHAR) modeData,
1413                                 MODE_PAGE_DATA_SIZE,
1414                                 MODE_PAGE_CACHING,
1415                                 MODE_SENSE_CHANGEABLE_VALUES);
1416 
1417         if (length < sizeof(MODE_PARAMETER_HEADER)) {
1418 
1419             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Mode Sense failed\n"));
1420 
1421             //
1422             // If the device fails to return changeable pages, then
1423             // set the write cache changeable value to unknown.
1424             //
1425 
1426             writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
1427             information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
1428             goto WriteCacheExit;
1429         }
1430     }
1431 
1432     //
1433     // If the length is greater than length indicated by the mode data reset
1434     // the data to the mode data.
1435     //
1436 
1437     if (length > (ULONG) (modeData->ModeDataLength + 1)) {
1438         length = modeData->ModeDataLength + 1;
1439     }
1440 
1441     //
1442     // Look for caching page in the returned mode page data.
1443     //
1444 
1445     pageData = ClassFindModePage((PCHAR) modeData,
1446                                  length,
1447                                  MODE_PAGE_CACHING,
1448                                  TRUE);
1449     //
1450     // Check if valid caching page exists.
1451     //
1452 
1453     if (pageData == NULL) {
1454         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to find caching mode page.\n"));
1455         //
1456         // Set write cache changeable value to unknown.
1457         //
1458         writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
1459     } else {
1460         writeCache->WriteCacheChangeable = pageData->WriteCacheEnable ?
1461                                             WriteCacheChangeable : WriteCacheNotChangeable;
1462     }
1463 
1464     information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
1465 
1466 WriteCacheExit:
1467 
1468     FREE_POOL(modeData);
1469 
1470     //
1471     // Set the size and status in IRP
1472     //
1473     Irp->IoStatus.Information = information;;
1474     Irp->IoStatus.Status = status;
1475 
1476     ClassReleaseRemoveLock(DeviceObject, Irp);
1477     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1478 
1479     return status;
1480 }
1481 
1482 ULONG
ClasspCalculateLogicalSectorSize(_In_ PDEVICE_OBJECT Fdo,_In_ ULONG BytesPerBlockInBigEndian)1483 ClasspCalculateLogicalSectorSize (
1484     _In_ PDEVICE_OBJECT Fdo,
1485     _In_ ULONG          BytesPerBlockInBigEndian
1486     )
1487 /*++
1488     Convert the big-endian value.
1489     if it's 0, default to the standard 512 bytes.
1490     if it's not a power of 2 value, round down to power of 2.
1491 --*/
1492 {
1493     ULONG logicalSectorSize;
1494 
1495     REVERSE_BYTES(&logicalSectorSize, &BytesPerBlockInBigEndian);
1496 
1497     if (logicalSectorSize == 0) {
1498         logicalSectorSize = 512;
1499     } else {
1500         //
1501         //  Clear all but the highest set bit.
1502         //  That will give us a bytesPerSector value that is a power of 2.
1503         //
1504         if (logicalSectorSize & (logicalSectorSize-1)) {
1505             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "FDO %ph has non-standard sector size 0x%x.", Fdo, logicalSectorSize));
1506             do {
1507                 logicalSectorSize &= logicalSectorSize-1;
1508             }
1509             while (logicalSectorSize & (logicalSectorSize-1));
1510         }
1511     }
1512 
1513     return logicalSectorSize;
1514 }
1515 
1516 NTSTATUS
InterpretReadCapacity16Data(_Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_In_ PREAD_CAPACITY16_DATA ReadCapacity16Data)1517 InterpretReadCapacity16Data (
1518     _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1519     _In_ PREAD_CAPACITY16_DATA ReadCapacity16Data
1520     )
1521 {
1522     NTSTATUS status = STATUS_SUCCESS;
1523     USHORT   lowestAlignedBlock;
1524     USHORT   logicalBlocksPerPhysicalBlock;
1525     PCLASS_READ_CAPACITY16_DATA cachedData = &(FdoExtension->FunctionSupportInfo->ReadCapacity16Data);
1526 
1527     // use Logical Sector Size from DiskGeometry to avoid duplicated calculation.
1528     FdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector = ClasspCalculateLogicalSectorSize(FdoExtension->DeviceObject, ReadCapacity16Data->BytesPerBlock);
1529 
1530     // FdoExtension->DiskGeometry.BytesPerSector might be 0 for class drivers that don't get READ CAPACITY info yet.
1531     NT_ASSERT( (FdoExtension->DiskGeometry.BytesPerSector == 0) ||
1532                (FdoExtension->DiskGeometry.BytesPerSector == FdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector) );
1533 
1534     logicalBlocksPerPhysicalBlock = 1 << ReadCapacity16Data->LogicalPerPhysicalExponent;
1535     lowestAlignedBlock = (ReadCapacity16Data->LowestAlignedBlock_MSB << 8) | ReadCapacity16Data->LowestAlignedBlock_LSB;
1536 
1537     if (lowestAlignedBlock > logicalBlocksPerPhysicalBlock) {
1538         // we get garbage data
1539         status = STATUS_UNSUCCESSFUL;
1540     } else {
1541         // value of lowestAlignedBlock (from T10 spec) needs to be converted.
1542         lowestAlignedBlock = (logicalBlocksPerPhysicalBlock - lowestAlignedBlock) % logicalBlocksPerPhysicalBlock;
1543     }
1544 
1545     if (NT_SUCCESS(status)) {
1546         // fill output buffer
1547         cachedData->BytesPerPhysicalSector = cachedData->BytesPerLogicalSector * logicalBlocksPerPhysicalBlock;
1548         cachedData->BytesOffsetForSectorAlignment = cachedData->BytesPerLogicalSector * lowestAlignedBlock;
1549 
1550         //
1551         // Fill in the Logical Block Provisioning info.  Note that we do not
1552         // use these fields; we use the Provisioning Type and LBPRZ fields from
1553         // the Logical Block Provisioning VPD page (0xB2).
1554         //
1555         cachedData->LBProvisioningEnabled = ReadCapacity16Data->LBPME;
1556         cachedData->LBProvisioningReadZeros = ReadCapacity16Data->LBPRZ;
1557 
1558         TracePrint((TRACE_LEVEL_INFORMATION,
1559                     TRACE_FLAG_INIT,
1560                     "InterpretReadCapacity16Data: Device\'s LBP enabled = %d\n",
1561                     cachedData->LBProvisioningEnabled));
1562     }
1563 
1564     return status;
1565 }
1566 
1567 NTSTATUS
ClassReadCapacity16(_Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_Inout_ PSCSI_REQUEST_BLOCK Srb)1568 ClassReadCapacity16 (
1569     _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1570     _Inout_ PSCSI_REQUEST_BLOCK Srb
1571     )
1572 /*
1573     This routine may send down a READ CAPACITY 16 command to retrieve info.
1574     The info will be cached in FdoExtension->LowerLayerSupport->AccessAlignment.
1575 
1576     After info retrieving finished, this function sets following field:
1577         FdoExtension->LowerLayerSupport->AccessAlignment.LowerLayerSupported = Supported;
1578     to indicate that info has been cached.
1579 
1580     NOTE: some future processes may use this function to send the command anyway, it will be caller's decision
1581           on checking 'AccessAlignment.LowerLayerSupported' in case the cached info is good enough.
1582 */
1583 {
1584     NTSTATUS              status = STATUS_SUCCESS;
1585     PREAD_CAPACITY16_DATA dataBuffer = NULL;
1586     UCHAR                 bufferLength = sizeof(READ_CAPACITY16_DATA);
1587     ULONG                 allocationBufferLength = bufferLength; //DMA buffer size for alignment
1588     PCDB                  cdb;
1589     ULONG                 dataTransferLength = 0;
1590 
1591     //
1592     // If the information retrieval has already been attempted, return the cached status.
1593     //
1594     if (FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus != -1) {
1595         // get cached NTSTATUS from previous call.
1596         return FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus;
1597     }
1598 
1599     if (ClasspIsObsoletePortDriver(FdoExtension)) {
1600         FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = STATUS_NOT_IMPLEMENTED;
1601         return STATUS_NOT_IMPLEMENTED;
1602     }
1603 
1604 #if defined(_ARM_) || defined(_ARM64_)
1605     //
1606     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
1607     // based platforms. We are taking the conservative approach here.
1608     //
1609     allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
1610     dataBuffer = (PREAD_CAPACITY16_DATA)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, '4CcS');
1611 #else
1612     dataBuffer = (PREAD_CAPACITY16_DATA)ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, '4CcS');
1613 #endif
1614 
1615     if (dataBuffer == NULL) {
1616         // return without updating FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus
1617         // the field will remain value as "-1", so that the command will be attempted next time this function is called.
1618         return STATUS_INSUFFICIENT_RESOURCES;
1619     }
1620 
1621     RtlZeroMemory(dataBuffer, allocationBufferLength);
1622 
1623     //
1624     // Initialize the SRB.
1625     //
1626     if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1627         status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
1628                                                 STORAGE_ADDRESS_TYPE_BTL8,
1629                                                 CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
1630                                                 1,
1631                                                 SrbExDataTypeScsiCdb16);
1632         if (NT_SUCCESS(status)) {
1633             ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
1634         } else {
1635             //
1636             // Should not occur.
1637             //
1638             NT_ASSERT(FALSE);
1639         }
1640     } else {
1641         RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
1642         Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
1643         Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
1644     }
1645 
1646     //prepare the Srb
1647     if (NT_SUCCESS(status))
1648     {
1649 
1650         SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
1651         SrbSetRequestTag(Srb, SP_UNTAGGED);
1652         SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
1653         SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
1654 
1655         SrbSetCdbLength(Srb, 16);
1656 
1657         cdb = SrbGetCdb(Srb);
1658         cdb->READ_CAPACITY16.OperationCode = SCSIOP_READ_CAPACITY16;
1659         cdb->READ_CAPACITY16.ServiceAction = SERVICE_ACTION_READ_CAPACITY16;
1660         cdb->READ_CAPACITY16.AllocationLength[3] = bufferLength;
1661 
1662         status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
1663                                          Srb,
1664                                          dataBuffer,
1665                                          allocationBufferLength,
1666                                          FALSE);
1667 
1668         dataTransferLength = SrbGetDataTransferLength(Srb);
1669     }
1670 
1671     if (NT_SUCCESS(status) && (dataTransferLength < 16))
1672     {
1673         // the device should return at least 16 bytes of data for this command.
1674         status = STATUS_INFO_LENGTH_MISMATCH;
1675     }
1676 
1677     //
1678     // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
1679     // buffer was larger than necessary.
1680     //
1681     if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength)
1682     {
1683         status = STATUS_SUCCESS;
1684     }
1685 
1686     if (NT_SUCCESS(status))
1687     {
1688         // cache data into FdoExtension
1689         status = InterpretReadCapacity16Data(FdoExtension, dataBuffer);
1690     }
1691 
1692     // cache the status indicates that this funciton has been called.
1693     FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = status;
1694 
1695     ExFreePool(dataBuffer);
1696 
1697     return status;
1698 }
1699 
ClasspAccessAlignmentProperty(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)1700 NTSTATUS ClasspAccessAlignmentProperty(
1701     _In_ PDEVICE_OBJECT DeviceObject,
1702     _In_ PIRP Irp,
1703     _Inout_ PSCSI_REQUEST_BLOCK Srb
1704     )
1705 /*
1706     At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
1707     If it's not supported, SCSIOP_READ_CAPACITY16 will be sent down to retrieve the information.
1708 */
1709 {
1710     NTSTATUS    status = STATUS_UNSUCCESSFUL;
1711 
1712     PCOMMON_DEVICE_EXTENSION     commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1713     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1714     PSTORAGE_PROPERTY_QUERY      query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
1715     PIO_STACK_LOCATION           irpStack = IoGetCurrentIrpStackLocation(Irp);
1716     ULONG                        length = 0;
1717     ULONG                        information = 0;
1718 
1719     PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR accessAlignment;
1720 
1721     //
1722     // check registry setting and fail the IOCTL if it's required.
1723     // this registry setting can be used to work around issues which upper layer doesn't support large physical sector size.
1724     //
1725     if (fdoExtension->FunctionSupportInfo->RegAccessAlignmentQueryNotSupported) {
1726         status = STATUS_NOT_SUPPORTED;
1727         goto Exit;
1728     }
1729 
1730     if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
1731          (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
1732          (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty == Supported) ) {
1733         // if it's not disk, forward the request to lower layer,
1734         // if the IOCTL is supported by lower stack, forward it down.
1735         IoCopyCurrentIrpStackLocationToNext(Irp);
1736 
1737         ClassReleaseRemoveLock(DeviceObject, Irp);
1738         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1739         return status;
1740     }
1741 
1742     //
1743     // Check proper query type.
1744     //
1745 
1746     if (query->QueryType == PropertyExistsQuery) {
1747         status = STATUS_SUCCESS;
1748         goto Exit;
1749     } else  if (query->QueryType != PropertyStandardQuery) {
1750         status = STATUS_NOT_SUPPORTED;
1751         goto Exit;
1752     }
1753 
1754     //
1755     // Request validation.
1756     // Note that InputBufferLength and IsFdo have been validated before entering this routine.
1757     //
1758 
1759     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
1760         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
1761         status = STATUS_INVALID_LEVEL;
1762         goto Exit;
1763     }
1764 
1765     // do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
1766     accessAlignment = (PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
1767 
1768     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
1769 
1770     if (length < sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)) {
1771 
1772         if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
1773 
1774             information = sizeof(STORAGE_DESCRIPTOR_HEADER);
1775             accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1776             accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1777             status = STATUS_SUCCESS;
1778             goto Exit;
1779         }
1780 
1781         status = STATUS_BUFFER_TOO_SMALL;
1782         goto Exit;
1783     }
1784 
1785     // not support Cache Line,
1786     // 'BytesPerCacheLine' and 'BytesOffsetForCacheAlignment' fields are zero-ed.
1787 
1788     //
1789     // note that 'Supported' case has been handled at the beginning of this function.
1790     //
1791     switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty) {
1792     case SupportUnknown: {
1793         // send down request and wait for the request to complete.
1794         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1795 
1796         if (ClasspLowerLayerNotSupport(status)) {
1797             // case 1: the request is not supported by lower layer, sends down command
1798             // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
1799 
1800             // ClassReadCapacity16() will either return status from cached data or send command to retrieve the information.
1801             if (ClasspIsObsoletePortDriver(fdoExtension) == FALSE) {
1802                 status = ClassReadCapacity16(fdoExtension, Srb);
1803             } else {
1804                 fdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = status;
1805             }
1806 
1807             // data is ready in fdoExtension
1808             // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
1809             fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty = NotSupported;
1810 
1811             if (NT_SUCCESS(status)) {
1812                 // fill output buffer
1813                 RtlZeroMemory(accessAlignment, length);
1814                 accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1815                 accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1816                 accessAlignment->BytesPerLogicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector;
1817                 accessAlignment->BytesPerPhysicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerPhysicalSector;
1818                 accessAlignment->BytesOffsetForSectorAlignment = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesOffsetForSectorAlignment;
1819 
1820                 // set returned data length
1821                 information = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1822             } else {
1823                 information = 0;
1824             }
1825 
1826         } else {
1827             // case 2: the request is supported and it completes successfully
1828             // case 3: the request is supported by lower stack but other failure status is returned.
1829             // from now on, the same request will be send down to lower stack directly.
1830             fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty = Supported;
1831             information = (ULONG)Irp->IoStatus.Information;
1832 
1833 
1834         }
1835 
1836 
1837         goto Exit;
1838 
1839         break;
1840     }
1841 
1842     case NotSupported: {
1843 
1844         // ClassReadCapacity16() will either return status from cached data or send command to retrieve the information.
1845         status = ClassReadCapacity16(fdoExtension, Srb);
1846 
1847         if (NT_SUCCESS(status)) {
1848             RtlZeroMemory(accessAlignment, length);
1849             accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1850             accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1851             accessAlignment->BytesPerLogicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector;
1852             accessAlignment->BytesPerPhysicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerPhysicalSector;
1853             accessAlignment->BytesOffsetForSectorAlignment = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesOffsetForSectorAlignment;
1854 
1855             information = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1856         } else {
1857             information = 0;
1858         }
1859         goto Exit;
1860 
1861         break;
1862     }
1863 
1864     case Supported: {
1865         NT_ASSERT(FALSE); // this case is handled at the beginning of the function.
1866         status = STATUS_INTERNAL_ERROR;
1867         break;
1868     }
1869 
1870     }   // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty)
1871 
1872 Exit:
1873 
1874     //
1875     // Set the size and status in IRP
1876     //
1877     Irp->IoStatus.Information = information;
1878     Irp->IoStatus.Status = status;
1879 
1880     ClassReleaseRemoveLock(DeviceObject, Irp);
1881     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1882 
1883     return status;
1884 }
1885 
1886 static
1887 NTSTATUS
IncursSeekPenalty(_In_ USHORT MediumRotationRate,_In_ PBOOLEAN IncursSeekPenalty)1888 IncursSeekPenalty (
1889     _In_ USHORT     MediumRotationRate,
1890     _In_ PBOOLEAN   IncursSeekPenalty
1891     )
1892 {
1893     NTSTATUS status;
1894 
1895     if (MediumRotationRate == 0x0001) {
1896         // Non-rotating media (e.g., solid state device)
1897         *IncursSeekPenalty = FALSE;
1898         status = STATUS_SUCCESS;
1899     } else if ( (MediumRotationRate >= 0x401) &&
1900                 (MediumRotationRate <= 0xFFFE) ) {
1901         // Nominal media rotation rate in rotations per minute (rpm)
1902         *IncursSeekPenalty = TRUE;
1903         status = STATUS_SUCCESS;
1904     } else {
1905         // Unknown cases:
1906         //    0 - Rate not reported
1907         //    0002h-0400h - Reserved
1908         //    FFFFh - Reserved
1909         status = STATUS_UNSUCCESSFUL;
1910     }
1911 
1912     return status;
1913 }
1914 
1915 
1916 NTSTATUS
ClasspDeviceMediaTypeProperty(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)1917 ClasspDeviceMediaTypeProperty(
1918     _In_ PDEVICE_OBJECT DeviceObject,
1919     _Inout_ PIRP Irp,
1920     _Inout_ PSCSI_REQUEST_BLOCK Srb
1921     )
1922 /*++
1923 
1924 Routine Description:
1925 
1926     This routine returns the medium product type reported by the device for the associated LU.
1927 
1928     This function must be called at IRQL < DISPATCH_LEVEL.
1929 
1930 Arguments:
1931 
1932     DeviceObject - Supplies the device object associated with this request
1933     Irp - The IRP to be processed
1934     Srb - The SRB associated with the request
1935 
1936 Return Value:
1937 
1938     NTSTATUS code
1939 
1940 --*/
1941 {
1942     NTSTATUS status = STATUS_UNSUCCESSFUL;
1943     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1944     PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
1945     PSTORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR pDesc = (PSTORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
1946     PIO_STACK_LOCATION irpStack;
1947     ULONG length = 0;
1948     ULONG information = 0;
1949 
1950     irpStack = IoGetCurrentIrpStackLocation(Irp);
1951 
1952     TracePrint((TRACE_LEVEL_VERBOSE,
1953                 TRACE_FLAG_IOCTL,
1954                 "ClasspDeviceMediaTypeProperty (%p): Entering function.\n",
1955                 DeviceObject));
1956 
1957     //
1958     // Check proper query type.
1959     //
1960     if (query->QueryType == PropertyExistsQuery) {
1961 
1962         //
1963         // In order to maintain consistency with the how the rest of the properties
1964         // are handled, always return success for PropertyExistsQuery.
1965         //
1966         status = STATUS_SUCCESS;
1967         goto __ClasspDeviceMediaTypeProperty_Exit;
1968 
1969     } else if (query->QueryType != PropertyStandardQuery) {
1970 
1971         TracePrint((TRACE_LEVEL_ERROR,
1972                     TRACE_FLAG_IOCTL,
1973                     "ClasspDeviceMediaTypeProperty (%p): Unsupported query type %x for media type property.\n",
1974                     DeviceObject,
1975                     query->QueryType));
1976 
1977         status = STATUS_NOT_SUPPORTED;
1978         goto __ClasspDeviceMediaTypeProperty_Exit;
1979     }
1980 
1981     //
1982     // Validate the request.
1983     // InputBufferLength and IsFdo have already been validated.
1984     //
1985 
1986     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
1987 
1988         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
1989 
1990         TracePrint((TRACE_LEVEL_ERROR,
1991                     TRACE_FLAG_IOCTL,
1992                     "ClasspDeviceMediaTypeProperty (%p): Query property for media type at incorrect IRQL.\n",
1993                     DeviceObject));
1994 
1995         status = STATUS_INVALID_LEVEL;
1996         goto __ClasspDeviceMediaTypeProperty_Exit;
1997     }
1998 
1999     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
2000 
2001     if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2002 
2003         information = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
2004         pDesc->Version = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
2005         pDesc->Size = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
2006     } else {
2007 
2008         status = STATUS_BUFFER_TOO_SMALL;
2009         goto __ClasspDeviceMediaTypeProperty_Exit;
2010     }
2011 
2012     if (length < sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR)) {
2013 
2014         status = STATUS_SUCCESS;
2015         goto __ClasspDeviceMediaTypeProperty_Exit;
2016     }
2017 
2018     //
2019     // Only query BlockDeviceCharacteristics VPD page if device support has been confirmed.
2020     //
2021     if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics == TRUE) {
2022         status = ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(fdoExtension, Srb);
2023     } else {
2024         //
2025         // Otherwise device was previously found lacking support for this VPD page. Fail the request.
2026         //
2027         status = STATUS_INVALID_DEVICE_REQUEST;
2028         goto __ClasspDeviceMediaTypeProperty_Exit;
2029     }
2030 
2031     if (!NT_SUCCESS(status)) {
2032 
2033         status = fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus;
2034         information = 0;
2035 
2036         TracePrint((TRACE_LEVEL_ERROR,
2037                     TRACE_FLAG_IOCTL,
2038                     "ClasspDeviceGetBlockDeviceCharacteristicsVPDPage (%p): VPD retrieval fails with %x.\n",
2039                     DeviceObject,
2040                     status));
2041 
2042         goto __ClasspDeviceMediaTypeProperty_Exit;
2043     }
2044 
2045     //
2046     // Fill in the output buffer.  All data is copied from the FDO extension, cached
2047     //  from device response to earlier VPD_BLOCK_DEVICE_CHARACTERISTICS query.
2048     //
2049     pDesc->MediumProductType = fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumProductType;
2050     status = STATUS_SUCCESS;
2051 
2052 __ClasspDeviceMediaTypeProperty_Exit:
2053 
2054     //
2055     // Set the size and status in IRP
2056     //
2057     Irp->IoStatus.Information = information;
2058     Irp->IoStatus.Status = status;
2059 
2060     ClassReleaseRemoveLock(DeviceObject, Irp);
2061     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2062 
2063     TracePrint((TRACE_LEVEL_VERBOSE,
2064                 TRACE_FLAG_IOCTL,
2065                 "ClasspDeviceMediaTypeProperty (%p): Exiting function with status %x.\n",
2066                 DeviceObject,
2067                 status));
2068 
2069     return status;
2070 }
2071 
ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(_In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension,_In_ PSCSI_REQUEST_BLOCK Srb)2072 NTSTATUS ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(
2073     _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension,
2074     _In_ PSCSI_REQUEST_BLOCK Srb
2075     )
2076 /*
2077 Routine Description:
2078 
2079     This function sends an INQUIRY command request for VPD_BLOCK_DEVICE_CHARACTERISTICS to
2080     the device. Relevant data from the response is cached in the FDO extension.
2081 
2082 Arguments:
2083     FdoExtension: The FDO extension of the device to which the INQUIRY command will be sent.
2084     Srb: Allocated by the caller.
2085     SrbSize: The size of the Srb buffer in bytes.
2086 
2087 Return Value:
2088 
2089     STATUS_INVALID_PARAMETER: May be returned if the LogPage buffer is NULL or
2090         not large enough.
2091     STATUS_SUCCESS: The log page was obtained and placed in the LogPage buffer.
2092 
2093     This function may return other NTSTATUS codes from internal function calls.
2094 --*/
2095 {
2096     NTSTATUS status = STATUS_UNSUCCESSFUL;
2097     PCDB cdb;
2098     UCHAR bufferLength = sizeof(VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE);  // data is 64 bytes
2099     ULONG allocationBufferLength = bufferLength;
2100     PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE dataBuffer = NULL;
2101 
2102 
2103 #if defined(_ARM_) || defined(_ARM64_)
2104     //
2105     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
2106     // based platforms. We are taking the conservative approach here.
2107     //
2108     allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
2109     dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
2110                                                                                 allocationBufferLength,
2111                                                                                 '5CcS'
2112                                                                                 );
2113 #else
2114 
2115     dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)ExAllocatePoolWithTag(NonPagedPoolNx,
2116                                                                                 bufferLength,
2117                                                                                 '5CcS'
2118                                                                                 );
2119 #endif
2120     if (dataBuffer == NULL) {
2121         status = STATUS_INSUFFICIENT_RESOURCES;
2122         goto Exit;
2123     }
2124 
2125     RtlZeroMemory(dataBuffer, allocationBufferLength);
2126 
2127     // prepare the Srb
2128     SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
2129     SrbSetRequestTag(Srb, SP_UNTAGGED);
2130     SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
2131     SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
2132 
2133     SrbSetCdbLength(Srb, 6);
2134 
2135     cdb = SrbGetCdb(Srb);
2136     cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
2137     cdb->CDB6INQUIRY3.EnableVitalProductData = 1;       //EVPD bit
2138     cdb->CDB6INQUIRY3.PageCode = VPD_BLOCK_DEVICE_CHARACTERISTICS;
2139     cdb->CDB6INQUIRY3.AllocationLength = bufferLength;  //AllocationLength field in CDB6INQUIRY3 is only one byte.
2140 
2141     status = ClassSendSrbSynchronous(fdoExtension->CommonExtension.DeviceObject,
2142                                         Srb,
2143                                         dataBuffer,
2144                                         allocationBufferLength,
2145                                         FALSE);
2146     if (NT_SUCCESS(status)) {
2147         if (SrbGetDataTransferLength(Srb) < 0x8) {
2148             // the device should return at least 8 bytes of data for use.
2149             status = STATUS_UNSUCCESSFUL;
2150         } else if ( (dataBuffer->PageLength != 0x3C) || (dataBuffer->PageCode != VPD_BLOCK_DEVICE_CHARACTERISTICS) ) {
2151             // 'PageLength' shall be 0x3C; and 'PageCode' shall match.
2152             status = STATUS_UNSUCCESSFUL;
2153         } else {
2154             // cache data into fdoExtension
2155             fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumRotationRate = (dataBuffer->MediumRotationRateMsb << 8) |
2156                                                                                                 dataBuffer->MediumRotationRateLsb;
2157             fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumProductType = dataBuffer->MediumProductType;
2158             fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.NominalFormFactor = dataBuffer->NominalFormFactor;
2159         }
2160     } else {
2161         // the command failed, surface up the command error from 'status' variable. Nothing to do here.
2162     }
2163 
2164 Exit:
2165     if (dataBuffer != NULL) {
2166         ExFreePool(dataBuffer);
2167     }
2168 
2169     return status;
2170 }
2171 
ClasspDeviceSeekPenaltyProperty(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)2172 NTSTATUS ClasspDeviceSeekPenaltyProperty(
2173     _In_ PDEVICE_OBJECT DeviceObject,
2174     _In_ PIRP Irp,
2175     _Inout_ PSCSI_REQUEST_BLOCK Srb
2176     )
2177 /*
2178     At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
2179     If it's not supported, INQUIRY (Block Device Characteristics VPD page) will be sent down to retrieve the information.
2180 */
2181 {
2182     NTSTATUS    status = STATUS_UNSUCCESSFUL;
2183 
2184     PCOMMON_DEVICE_EXTENSION     commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2185     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2186     PSTORAGE_PROPERTY_QUERY      query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
2187     PIO_STACK_LOCATION           irpStack = IoGetCurrentIrpStackLocation(Irp);
2188     ULONG                        length = 0;
2189     ULONG                        information = 0;
2190     BOOLEAN                      incursSeekPenalty = TRUE;
2191     PDEVICE_SEEK_PENALTY_DESCRIPTOR seekPenalty;
2192 
2193     if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
2194          (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
2195          (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty == Supported) ) {
2196         // if it's not disk, forward the request to lower layer,
2197         // if the IOCTL is supported by lower stack, forward it down.
2198         IoCopyCurrentIrpStackLocationToNext(Irp);
2199 
2200         ClassReleaseRemoveLock(DeviceObject, Irp);
2201         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
2202         return status;
2203     }
2204 
2205     //
2206     // Check proper query type.
2207     //
2208 
2209     if (query->QueryType == PropertyExistsQuery) {
2210         status = STATUS_SUCCESS;
2211         goto Exit;
2212     } else  if (query->QueryType != PropertyStandardQuery) {
2213         status = STATUS_NOT_SUPPORTED;
2214         goto Exit;
2215     }
2216 
2217     //
2218     // Request validation.
2219     // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
2220     //
2221 
2222     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
2223         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
2224         status = STATUS_INVALID_LEVEL;
2225         goto Exit;
2226     }
2227 
2228     // do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
2229     seekPenalty = (PDEVICE_SEEK_PENALTY_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
2230 
2231     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
2232 
2233     if (length < sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR)) {
2234 
2235         if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2236 
2237             information = sizeof(STORAGE_DESCRIPTOR_HEADER);
2238             seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2239             seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2240             status = STATUS_SUCCESS;
2241             goto Exit;
2242         }
2243 
2244         status = STATUS_BUFFER_TOO_SMALL;
2245         goto Exit;
2246     }
2247 
2248     //
2249     // note that 'Supported' case has been handled at the beginning of this function.
2250     //
2251     switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty) {
2252     case SupportUnknown: {
2253         // send down request and wait for the request to complete.
2254         status = ClassForwardIrpSynchronous(commonExtension, Irp);
2255 
2256         if (ClasspLowerLayerNotSupport(status)) {
2257             // case 1: the request is not supported by lower layer, sends down command
2258             // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
2259 
2260             // send INQUIRY command if the VPD page is supported.
2261             if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics == TRUE) {
2262                 status = ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(fdoExtension, Srb);
2263             } else {
2264                 // the INQUIRY - VPD page command to discover the info is not supported, fail the request.
2265                 status = STATUS_INVALID_DEVICE_REQUEST;
2266             }
2267 
2268             if (NT_SUCCESS(status)) {
2269                 status = IncursSeekPenalty(fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumRotationRate, &incursSeekPenalty);
2270             }
2271 
2272             fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus = status;
2273 
2274             // data is ready in fdoExtension
2275             // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
2276             fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty = NotSupported;
2277 
2278             // fill output buffer
2279             if (NT_SUCCESS(status)) {
2280                 RtlZeroMemory(seekPenalty, length);
2281                 seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2282                 seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2283                 seekPenalty->IncursSeekPenalty = incursSeekPenalty;
2284                 information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2285 
2286 
2287             } else {
2288                 information = 0;
2289             }
2290 
2291         } else {
2292             // case 2: the request is supported and it completes successfully
2293             // case 3: the request is supported by lower stack but other failure status is returned.
2294             // from now on, the same request will be send down to lower stack directly.
2295             fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty = Supported;
2296             information = (ULONG)Irp->IoStatus.Information;
2297 
2298         }
2299 
2300 
2301         goto Exit;
2302 
2303         break;
2304     }
2305 
2306     case NotSupported: {
2307         status = fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus;
2308 
2309         if (NT_SUCCESS(status)) {
2310             status = IncursSeekPenalty(fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumRotationRate, &incursSeekPenalty);
2311         }
2312 
2313         if (NT_SUCCESS(status)) {
2314             RtlZeroMemory(seekPenalty, length);
2315             seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2316             seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2317             seekPenalty->IncursSeekPenalty = incursSeekPenalty;
2318             information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2319 
2320         } else {
2321             information = 0;
2322         }
2323 
2324         goto Exit;
2325 
2326         break;
2327     }
2328 
2329     case Supported: {
2330         NT_ASSERT(FALSE); // this case is handled at the begining of the function.
2331         break;
2332     }
2333 
2334     }   // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty)
2335 
2336 Exit:
2337 
2338     //
2339     // Set the size and status in IRP
2340     //
2341     Irp->IoStatus.Information = information;;
2342     Irp->IoStatus.Status = status;
2343 
2344     ClassReleaseRemoveLock(DeviceObject, Irp);
2345     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2346 
2347     return status;
2348 }
2349 
ClasspDeviceGetLBProvisioningVPDPage(_In_ PDEVICE_OBJECT DeviceObject,_Inout_opt_ PSCSI_REQUEST_BLOCK Srb)2350 NTSTATUS ClasspDeviceGetLBProvisioningVPDPage(
2351     _In_ PDEVICE_OBJECT DeviceObject,
2352     _Inout_opt_ PSCSI_REQUEST_BLOCK Srb
2353     )
2354 {
2355     NTSTATUS                     status = STATUS_UNSUCCESSFUL;
2356     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2357     USHORT                       pageLength = 0;
2358 
2359     PVOID                        dataBuffer = NULL;
2360     UCHAR                        bufferLength = VPD_MAX_BUFFER_SIZE;  // use biggest buffer possible
2361     ULONG                        allocationBufferLength = bufferLength; // Since the CDB size may differ from the actual buffer allocation
2362     PCDB                         cdb;
2363     ULONG                        dataTransferLength = 0;
2364     PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE  lbProvisioning = NULL;
2365 
2366     //
2367     // if the informaiton has been attempted to retrieve, return the cached status.
2368     //
2369     if (fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus != -1) {
2370         // get cached NTSTATUS from previous call.
2371         return fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus;
2372     }
2373 
2374     //
2375     // Initialize LBProvisioningData fields to 'unsupported' defaults.
2376     //
2377     fdoExtension->FunctionSupportInfo->LBProvisioningData.ProvisioningType = PROVISIONING_TYPE_UNKNOWN;
2378     fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ = FALSE;
2379     fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU = FALSE;
2380     fdoExtension->FunctionSupportInfo->LBProvisioningData.ANC_SUP = FALSE;
2381     fdoExtension->FunctionSupportInfo->LBProvisioningData.ThresholdExponent = 0;
2382 
2383     //
2384     // Try to get the Thin Provisioning VPD page (0xB2), if it is supported.
2385     //
2386     if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE &&
2387         Srb != NULL)
2388     {
2389 #if defined(_ARM_) || defined(_ARM64_)
2390         //
2391         // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
2392         // based platforms. We are taking the conservative approach here.
2393         //
2394         //
2395         allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
2396         dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength,'0CcS');
2397 #else
2398         dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength,'0CcS');
2399 #endif
2400         if (dataBuffer == NULL) {
2401             // return without updating FdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus
2402             // the field will remain value as "-1", so that the command will be attempted next time this function is called.
2403             status = STATUS_INSUFFICIENT_RESOURCES;
2404             goto Exit;
2405         }
2406 
2407         lbProvisioning = (PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE)dataBuffer;
2408 
2409         RtlZeroMemory(dataBuffer, allocationBufferLength);
2410 
2411         if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
2412             status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
2413                                                    STORAGE_ADDRESS_TYPE_BTL8,
2414                                                    CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
2415                                                    1,
2416                                                    SrbExDataTypeScsiCdb16);
2417             if (NT_SUCCESS(status)) {
2418                 ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
2419             } else {
2420                 //
2421                 // Should not occur.
2422                 //
2423                 NT_ASSERT(FALSE);
2424             }
2425         } else {
2426             RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
2427             Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2428             Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2429             status = STATUS_SUCCESS;
2430         }
2431 
2432         if (NT_SUCCESS(status)) {
2433             // prepare the Srb
2434             SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
2435             SrbSetRequestTag(Srb, SP_UNTAGGED);
2436             SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
2437             SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
2438 
2439             SrbSetCdbLength(Srb, 6);
2440 
2441             cdb = SrbGetCdb(Srb);
2442             cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
2443             cdb->CDB6INQUIRY3.EnableVitalProductData = 1;       //EVPD bit
2444             cdb->CDB6INQUIRY3.PageCode = VPD_LOGICAL_BLOCK_PROVISIONING;
2445             cdb->CDB6INQUIRY3.AllocationLength = bufferLength;  //AllocationLength field in CDB6INQUIRY3 is only one byte.
2446 
2447             status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
2448                                              Srb,
2449                                              dataBuffer,
2450                                              allocationBufferLength,
2451                                              FALSE);
2452 
2453             dataTransferLength = SrbGetDataTransferLength(Srb);
2454         }
2455 
2456         //
2457         // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
2458         // buffer was larger than necessary.
2459         //
2460         if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength)
2461         {
2462             status = STATUS_SUCCESS;
2463         }
2464 
2465         if (NT_SUCCESS(status)) {
2466             REVERSE_BYTES_SHORT(&pageLength, &(lbProvisioning->PageLength));
2467         }
2468 
2469         if ( NT_SUCCESS(status) &&
2470                 ((dataTransferLength < 0x08) ||
2471                 (pageLength < (FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE, Reserved2) - FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE,ThresholdExponent))) ||
2472                 (lbProvisioning->PageCode != VPD_LOGICAL_BLOCK_PROVISIONING)) ) {
2473             // the device should return at least 8 bytes of data for use.
2474             // 'PageCode' shall match and we need all the relevant data after the header.
2475             status = STATUS_INFO_LENGTH_MISMATCH;
2476         }
2477 
2478         //
2479         // Fill in the FDO extension with either the data from the VPD page, or
2480         // use defaults if there was an error.
2481         //
2482         if (NT_SUCCESS(status))
2483         {
2484             fdoExtension->FunctionSupportInfo->LBProvisioningData.ProvisioningType = lbProvisioning->ProvisioningType;
2485             fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ = lbProvisioning->LBPRZ;
2486             fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU = lbProvisioning->LBPU;
2487             fdoExtension->FunctionSupportInfo->LBProvisioningData.ANC_SUP = lbProvisioning->ANC_SUP;
2488             fdoExtension->FunctionSupportInfo->LBProvisioningData.ThresholdExponent = lbProvisioning->ThresholdExponent;
2489 
2490             TracePrint((TRACE_LEVEL_INFORMATION,
2491                         TRACE_FLAG_PNP,
2492                         "ClasspDeviceGetLBProvisioningVPDPage (%p): %s %s (rev %s) reported following parameters: \
2493                         \n\t\t\tProvisioningType: %u \
2494                         \n\t\t\tLBPRZ: %u \
2495                         \n\t\t\tLBPU: %u \
2496                         \n\t\t\tANC_SUP: %I64u \
2497                         \n\t\t\tThresholdExponent: %u\n",
2498                         DeviceObject,
2499                         (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->VendorIdOffset),
2500                         (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductIdOffset),
2501                         (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductRevisionOffset),
2502                         lbProvisioning->ProvisioningType,
2503                         lbProvisioning->LBPRZ,
2504                         lbProvisioning->LBPU,
2505                         lbProvisioning->ANC_SUP,
2506                         lbProvisioning->ThresholdExponent));
2507         }
2508     } else {
2509         status = STATUS_INVALID_DEVICE_REQUEST;
2510     }
2511 
2512     fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = status;
2513 
2514 Exit:
2515     FREE_POOL(dataBuffer);
2516 
2517     return status;
2518 }
2519 
2520 
ClasspDeviceGetBlockLimitsVPDPage(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_Inout_bytecount_ (SrbSize)PSCSI_REQUEST_BLOCK Srb,_In_ ULONG SrbSize,_Out_ PCLASS_VPD_B0_DATA BlockLimitsData)2521 NTSTATUS ClasspDeviceGetBlockLimitsVPDPage(
2522     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2523     _Inout_bytecount_(SrbSize) PSCSI_REQUEST_BLOCK Srb,
2524     _In_ ULONG SrbSize,
2525     _Out_ PCLASS_VPD_B0_DATA BlockLimitsData
2526     )
2527 {
2528     NTSTATUS                     status = STATUS_UNSUCCESSFUL;
2529     PVOID                        dataBuffer = NULL;
2530     UCHAR                        bufferLength = VPD_MAX_BUFFER_SIZE;  // use biggest buffer possible
2531     ULONG                        allocationBufferLength = bufferLength;
2532     PCDB                         cdb;
2533     PVPD_BLOCK_LIMITS_PAGE       blockLimits = NULL;
2534     ULONG                        dataTransferLength = 0;
2535 
2536     //
2537     // Set default values for UNMAP parameters based upon UNMAP support or lack
2538     // thereof.
2539     //
2540     if (FdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU) {
2541         //
2542         // If UNMAP is supported, we default to the maximum LBA count and
2543         // block descriptor count.  We also default the UNMAP granularity to
2544         // a single block and specify no granularity alignment.
2545         //
2546         BlockLimitsData->MaxUnmapLbaCount = (ULONG)-1;
2547         BlockLimitsData->MaxUnmapBlockDescrCount = (ULONG)-1;
2548         BlockLimitsData->OptimalUnmapGranularity = 1;
2549         BlockLimitsData->UnmapGranularityAlignment = 0;
2550         BlockLimitsData->UGAVALID = FALSE;
2551     } else {
2552         BlockLimitsData->MaxUnmapLbaCount = 0;
2553         BlockLimitsData->MaxUnmapBlockDescrCount = 0;
2554         BlockLimitsData->OptimalUnmapGranularity = 0;
2555         BlockLimitsData->UnmapGranularityAlignment = 0;
2556         BlockLimitsData->UGAVALID = FALSE;
2557     }
2558 
2559     //
2560     // Try to get the Block Limits VPD page (0xB0), if it is supported.
2561     //
2562     if (FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE)
2563     {
2564 #if defined(_ARM_) || defined(_ARM64_)
2565         //
2566         // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
2567         // based platforms. We are taking the conservative approach here.
2568         //
2569         allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment());
2570         dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, '0CcS');
2571 #else
2572         dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, '0CcS');
2573 #endif
2574         if (dataBuffer == NULL)
2575         {
2576             // return without updating FdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus
2577             // the field will remain value as "-1", so that the command will be attempted next time this function is called.
2578             status = STATUS_INSUFFICIENT_RESOURCES;
2579             goto Exit;
2580         }
2581 
2582         blockLimits = (PVPD_BLOCK_LIMITS_PAGE)dataBuffer;
2583 
2584         RtlZeroMemory(dataBuffer, allocationBufferLength);
2585 
2586         if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
2587 
2588 #ifdef _MSC_VER
2589             #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
2590 #endif
2591             status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
2592                                                    STORAGE_ADDRESS_TYPE_BTL8,
2593                                                    SrbSize,
2594                                                    1,
2595                                                    SrbExDataTypeScsiCdb16);
2596 
2597             if (NT_SUCCESS(status)) {
2598                 ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
2599             } else {
2600                 //
2601                 // Should not occur.
2602                 //
2603                 NT_ASSERT(FALSE);
2604             }
2605         } else {
2606             RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
2607             Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2608             Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2609             status = STATUS_SUCCESS;
2610         }
2611 
2612         if (NT_SUCCESS(status)) {
2613             // prepare the Srb
2614             SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
2615             SrbSetRequestTag(Srb, SP_UNTAGGED);
2616             SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
2617             SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
2618 
2619             SrbSetCdbLength(Srb, 6);
2620 
2621             cdb = SrbGetCdb(Srb);
2622             cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
2623             cdb->CDB6INQUIRY3.EnableVitalProductData = 1;       //EVPD bit
2624             cdb->CDB6INQUIRY3.PageCode = VPD_BLOCK_LIMITS;
2625             cdb->CDB6INQUIRY3.AllocationLength = bufferLength;  //AllocationLength field in CDB6INQUIRY3 is only one byte.
2626 
2627             status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
2628                                              Srb,
2629                                              dataBuffer,
2630                                              allocationBufferLength,
2631                                              FALSE);
2632             dataTransferLength = SrbGetDataTransferLength(Srb);
2633         }
2634 
2635         //
2636         // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
2637         // buffer was larger than necessary.
2638         //
2639 
2640         if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength)
2641         {
2642             status = STATUS_SUCCESS;
2643         }
2644 
2645         if (NT_SUCCESS(status))
2646         {
2647             USHORT pageLength;
2648             REVERSE_BYTES_SHORT(&pageLength, &(blockLimits->PageLength));
2649 
2650             //
2651             // Regardless of the device's support for unmap, cache away at least the basic block limits information
2652             //
2653             if (dataTransferLength >= 0x10 && blockLimits->PageCode == VPD_BLOCK_LIMITS) {
2654 
2655                 // (6:7) OPTIMAL TRANSFER LENGTH GRANULARITY
2656                 REVERSE_BYTES_SHORT(&BlockLimitsData->OptimalTransferLengthGranularity, &blockLimits->OptimalTransferLengthGranularity);
2657                 // (8:11) MAXIMUM TRANSFER LENGTH
2658                 REVERSE_BYTES(&BlockLimitsData->MaximumTransferLength, &blockLimits->MaximumTransferLength);
2659                 // (12:15) OPTIMAL TRANSFER LENGTH
2660                 REVERSE_BYTES(&BlockLimitsData->OptimalTransferLength, &blockLimits->OptimalTransferLength);
2661             }
2662 
2663             if ((dataTransferLength < 0x24) ||
2664                 (pageLength < (FIELD_OFFSET(VPD_BLOCK_LIMITS_PAGE,Reserved1) - FIELD_OFFSET(VPD_BLOCK_LIMITS_PAGE,Reserved0))) ||
2665                 (blockLimits->PageCode != VPD_BLOCK_LIMITS))
2666             {
2667                 // the device should return at least 36 bytes of data for use.
2668                 // 'PageCode' shall match and we need all the relevant data after the header.
2669                 status = STATUS_INFO_LENGTH_MISMATCH;
2670             }
2671         }
2672 
2673         if (NT_SUCCESS(status))
2674         {
2675             // cache data into FdoExtension
2676             // (20:23) MAXIMUM UNMAP LBA COUNT
2677             REVERSE_BYTES(&BlockLimitsData->MaxUnmapLbaCount, &blockLimits->MaximumUnmapLBACount);
2678             // (24:27) MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT
2679             REVERSE_BYTES(&BlockLimitsData->MaxUnmapBlockDescrCount, &blockLimits->MaximumUnmapBlockDescriptorCount);
2680             // (28:31) OPTIMAL UNMAP GRANULARITY
2681             REVERSE_BYTES(&BlockLimitsData->OptimalUnmapGranularity, &blockLimits->OptimalUnmapGranularity);
2682 
2683             // (32:35) UNMAP GRANULARITY ALIGNMENT; (32) bit7: UGAVALID
2684             BlockLimitsData->UGAVALID = blockLimits->UGAValid;
2685             if (BlockLimitsData->UGAVALID == TRUE) {
2686                 REVERSE_BYTES(&BlockLimitsData->UnmapGranularityAlignment, &blockLimits->UnmapGranularityAlignment);
2687                 BlockLimitsData->UnmapGranularityAlignment &= 0x7FFFFFFF; // remove value of UGAVALID bit.
2688             } else {
2689                 BlockLimitsData->UnmapGranularityAlignment = 0;
2690             }
2691 
2692             TracePrint((TRACE_LEVEL_INFORMATION,
2693                         TRACE_FLAG_PNP,
2694                         "ClasspDeviceGetBlockLimitsVPDPage (%p): %s %s (rev %s) reported following parameters: \
2695                         \n\t\t\tOptimalTransferLengthGranularity: %u \
2696                         \n\t\t\tMaximumTransferLength: %u \
2697                         \n\t\t\tOptimalTransferLength: %u \
2698                         \n\t\t\tMaximumUnmapLBACount: %u \
2699                         \n\t\t\tMaximumUnmapBlockDescriptorCount: %u \
2700                         \n\t\t\tOptimalUnmapGranularity: %u \
2701                         \n\t\t\tUGAValid: %u \
2702                         \n\t\t\tUnmapGranularityAlignment: %u\n",
2703                         FdoExtension->DeviceObject,
2704                         (PCSZ)(((PUCHAR)FdoExtension->DeviceDescriptor) + FdoExtension->DeviceDescriptor->VendorIdOffset),
2705                         (PCSZ)(((PUCHAR)FdoExtension->DeviceDescriptor) + FdoExtension->DeviceDescriptor->ProductIdOffset),
2706                         (PCSZ)(((PUCHAR)FdoExtension->DeviceDescriptor) + FdoExtension->DeviceDescriptor->ProductRevisionOffset),
2707                         BlockLimitsData->OptimalTransferLengthGranularity,
2708                         BlockLimitsData->MaximumTransferLength,
2709                         BlockLimitsData->OptimalTransferLength,
2710                         BlockLimitsData->MaxUnmapLbaCount,
2711                         BlockLimitsData->MaxUnmapBlockDescrCount,
2712                         BlockLimitsData->OptimalUnmapGranularity,
2713                         BlockLimitsData->UGAVALID,
2714                         BlockLimitsData->UnmapGranularityAlignment));
2715 
2716         }
2717     } else {
2718         status = STATUS_INVALID_DEVICE_REQUEST;
2719     }
2720 
2721     BlockLimitsData->CommandStatus = status;
2722 
2723 Exit:
2724     FREE_POOL(dataBuffer);
2725 
2726     return status;
2727 }
2728 
2729 
ClasspDeviceTrimProperty(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)2730 NTSTATUS ClasspDeviceTrimProperty(
2731     _In_ PDEVICE_OBJECT DeviceObject,
2732     _In_ PIRP Irp,
2733     _Inout_ PSCSI_REQUEST_BLOCK Srb
2734     )
2735 /*
2736     At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
2737     If it's not supported, INQUIRY (Block Limits VPD page) will be sent down to retrieve the information.
2738 */
2739 {
2740     NTSTATUS    status = STATUS_SUCCESS;
2741 
2742     PCOMMON_DEVICE_EXTENSION     commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2743     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2744     PSTORAGE_PROPERTY_QUERY      query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
2745     PIO_STACK_LOCATION           irpStack = IoGetCurrentIrpStackLocation(Irp);
2746     ULONG                        length = 0;
2747     ULONG                        information = 0;
2748 
2749     PDEVICE_TRIM_DESCRIPTOR      trimDescr;
2750 
2751     UNREFERENCED_PARAMETER(Srb);
2752 
2753     if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
2754          (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
2755          (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty == Supported) ) {
2756         // if it's not disk, forward the request to lower layer,
2757         // if the IOCTL is supported by lower stack, forward it down.
2758         IoCopyCurrentIrpStackLocationToNext(Irp);
2759 
2760         ClassReleaseRemoveLock(DeviceObject, Irp);
2761         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
2762         return status;
2763     }
2764 
2765     //
2766     // Check proper query type.
2767     //
2768 
2769     if (query->QueryType == PropertyExistsQuery) {
2770         status = STATUS_SUCCESS;
2771         goto Exit;
2772     } else  if (query->QueryType != PropertyStandardQuery) {
2773         status = STATUS_NOT_SUPPORTED;
2774         goto Exit;
2775     }
2776 
2777     //
2778     // Request validation.
2779     // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
2780     //
2781 
2782     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
2783         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
2784         status = STATUS_INVALID_LEVEL;
2785         goto Exit;
2786     }
2787 
2788     // do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
2789     trimDescr = (PDEVICE_TRIM_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
2790 
2791     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
2792 
2793     if (length < sizeof(DEVICE_TRIM_DESCRIPTOR)) {
2794 
2795         if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2796 
2797             information = sizeof(STORAGE_DESCRIPTOR_HEADER);
2798             trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
2799             trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
2800             status = STATUS_SUCCESS;
2801             goto Exit;
2802         }
2803 
2804         status = STATUS_BUFFER_TOO_SMALL;
2805         goto Exit;
2806     }
2807 
2808     //
2809     // note that 'Supported' case has been handled at the beginning of this function.
2810     //
2811     switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty) {
2812     case SupportUnknown: {
2813         // send down request and wait for the request to complete.
2814         status = ClassForwardIrpSynchronous(commonExtension, Irp);
2815 
2816         if ( (status == STATUS_NOT_SUPPORTED) ||
2817              (status == STATUS_NOT_IMPLEMENTED) ||
2818              (status == STATUS_INVALID_DEVICE_REQUEST) ||
2819              (status == STATUS_INVALID_PARAMETER_1) ) {
2820             // case 1: the request is not supported by lower layer, sends down command
2821             // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
2822             status = fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus;
2823             NT_ASSERT(status != -1);
2824 
2825             // data is ready in fdoExtension
2826             // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
2827             fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty = NotSupported;
2828 
2829             if (NT_SUCCESS(status)) {
2830                 // fill output buffer
2831                 RtlZeroMemory(trimDescr, length);
2832                 trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
2833                 trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
2834                 trimDescr->TrimEnabled = ClasspSupportsUnmap(fdoExtension->FunctionSupportInfo);
2835 
2836                 // set returned data length
2837                 information = sizeof(DEVICE_TRIM_DESCRIPTOR);
2838             } else {
2839                 // there was error retrieving TrimProperty. Surface the error up from 'status' variable.
2840                 information = 0;
2841             }
2842             goto Exit;
2843         } else {
2844             // case 2: the request is supported and it completes successfully
2845             // case 3: the request is supported by lower stack but other failure status is returned.
2846             // from now on, the same request will be send down to lower stack directly.
2847             fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty = Supported;
2848             information = (ULONG)Irp->IoStatus.Information;
2849             goto Exit;
2850         }
2851         break;
2852     }
2853 
2854     case NotSupported: {
2855         status = fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus;
2856         NT_ASSERT(status != -1);
2857 
2858         if (NT_SUCCESS(status)) {
2859             RtlZeroMemory(trimDescr, length);
2860             trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
2861             trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
2862             trimDescr->TrimEnabled = ClasspSupportsUnmap(fdoExtension->FunctionSupportInfo);
2863 
2864             information = sizeof(DEVICE_TRIM_DESCRIPTOR);
2865         } else {
2866             information = 0;
2867         }
2868         goto Exit;
2869 
2870         break;
2871     }
2872 
2873     case Supported: {
2874         NT_ASSERT(FALSE); // this case is handled at the begining of the function.
2875         break;
2876     }
2877 
2878     }   // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty)
2879 
2880 Exit:
2881 
2882     //
2883     // Set the size and status in IRP
2884     //
2885     Irp->IoStatus.Information = information;
2886     Irp->IoStatus.Status = status;
2887 
2888     ClassReleaseRemoveLock(DeviceObject, Irp);
2889     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2890 
2891     return status;
2892 }
2893 
ClasspDeviceLBProvisioningProperty(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)2894 NTSTATUS ClasspDeviceLBProvisioningProperty(
2895     _In_ PDEVICE_OBJECT DeviceObject,
2896     _Inout_ PIRP Irp,
2897     _Inout_ PSCSI_REQUEST_BLOCK Srb
2898     )
2899 {
2900     NTSTATUS                     status = STATUS_SUCCESS;
2901     NTSTATUS                     blockLimitsStatus;
2902     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2903     PSTORAGE_PROPERTY_QUERY      query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
2904     PIO_STACK_LOCATION           irpStack = IoGetCurrentIrpStackLocation(Irp);
2905     ULONG                        length = 0;
2906     ULONG                        information = 0;
2907     CLASS_VPD_B0_DATA            blockLimitsData;
2908     ULONG                        generationCount;
2909 
2910     PDEVICE_LB_PROVISIONING_DESCRIPTOR  lbpDescr;
2911 
2912     UNREFERENCED_PARAMETER(Srb);
2913 
2914     //
2915     // Check proper query type.
2916     //
2917     if (query->QueryType == PropertyExistsQuery) {
2918         status = STATUS_SUCCESS;
2919         goto Exit;
2920     } else  if (query->QueryType != PropertyStandardQuery) {
2921         status = STATUS_NOT_SUPPORTED;
2922         goto Exit;
2923     }
2924 
2925     //
2926     // Request validation.
2927     // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
2928     //
2929 
2930     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
2931         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
2932         status = STATUS_INVALID_LEVEL;
2933         goto Exit;
2934     }
2935 
2936     lbpDescr = (PDEVICE_LB_PROVISIONING_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
2937 
2938     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
2939 
2940     RtlZeroMemory(lbpDescr, length);
2941 
2942     if (length < DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE) {
2943 
2944         if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2945 
2946             information = sizeof(STORAGE_DESCRIPTOR_HEADER);
2947             lbpDescr->Version = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2948             lbpDescr->Size = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2949             status = STATUS_SUCCESS;
2950             goto Exit;
2951         }
2952 
2953         status = STATUS_BUFFER_TOO_SMALL;
2954         goto Exit;
2955     }
2956 
2957     //
2958     // Set the structure version/size based upon the size of the given output
2959     // buffer.  We may be working with an older component that was built with
2960     // the V1 structure definition.
2961     //
2962     if (length < sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR)) {
2963         lbpDescr->Version = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE;
2964         lbpDescr->Size = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE;
2965         information = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE;
2966     } else {
2967         lbpDescr->Version = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2968         lbpDescr->Size = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2969         information = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2970     }
2971 
2972     //
2973     // Take a snapshot of the block limits data since it can change.
2974     // If we failed to get the block limits data, we'll just set the Optimal
2975     // Unmap Granularity (and alignment) will default to 0.  We don't want to
2976     // fail the request outright since there is some non-block limits data that
2977     // we can return.
2978     //
2979     blockLimitsStatus = ClasspBlockLimitsDataSnapshot(fdoExtension,
2980                                                       TRUE,
2981                                                       &blockLimitsData,
2982                                                       &generationCount);
2983 
2984     //
2985     // Fill in the output buffer.  All data is copied from the FDO extension where we
2986     // cached Logical Block Provisioning info when the device was first initialized.
2987     //
2988 
2989     lbpDescr->ThinProvisioningEnabled = ClasspIsThinProvisioned(fdoExtension->FunctionSupportInfo);
2990 
2991     //
2992     // Make sure we have a non-zero value for the number of bytes per block.
2993     //
2994     if (fdoExtension->DiskGeometry.BytesPerSector == 0)
2995     {
2996         status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
2997         if(!NT_SUCCESS(status) || fdoExtension->DiskGeometry.BytesPerSector == 0)
2998         {
2999             status = STATUS_INVALID_DEVICE_REQUEST;
3000             information = 0;
3001             goto Exit;
3002         }
3003     }
3004 
3005     lbpDescr->ThinProvisioningReadZeros = fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ;
3006     lbpDescr->AnchorSupported = fdoExtension->FunctionSupportInfo->LBProvisioningData.ANC_SUP;
3007 
3008     if (NT_SUCCESS(blockLimitsStatus)) {
3009         lbpDescr->UnmapGranularityAlignmentValid = blockLimitsData.UGAVALID;
3010 
3011         //
3012         // Granularity and Alignment are given to us in units of blocks,
3013         // but we convert and return them in bytes as it is more convenient
3014         // to the caller.
3015         //
3016         lbpDescr->OptimalUnmapGranularity = (ULONGLONG)blockLimitsData.OptimalUnmapGranularity * fdoExtension->DiskGeometry.BytesPerSector;
3017         lbpDescr->UnmapGranularityAlignment = (ULONGLONG)blockLimitsData.UnmapGranularityAlignment * fdoExtension->DiskGeometry.BytesPerSector;
3018 
3019 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3020         //
3021         // If the output buffer is large enough (i.e. not a V1 structure) copy
3022         // over the max UNMAP LBA count and max UNMAP block descriptor count.
3023         //
3024         if (length >= sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR)) {
3025             lbpDescr->MaxUnmapLbaCount = blockLimitsData.MaxUnmapLbaCount;
3026             lbpDescr->MaxUnmapBlockDescriptorCount = blockLimitsData.MaxUnmapBlockDescrCount;
3027         }
3028 #endif
3029     }
3030 
3031 Exit:
3032 
3033     //
3034     // Set the size and status in IRP
3035     //
3036     Irp->IoStatus.Information = information;
3037     Irp->IoStatus.Status = status;
3038 
3039     ClassReleaseRemoveLock(DeviceObject, Irp);
3040     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
3041 
3042     return status;
3043 }
3044 
3045 
3046 VOID
ConvertDataSetRangeToUnmapBlockDescr(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_In_ PUNMAP_BLOCK_DESCRIPTOR BlockDescr,_Inout_ PULONG CurrentBlockDescrIndex,_In_ ULONG MaxBlockDescrIndex,_Inout_ PULONGLONG CurrentLbaCount,_In_ ULONGLONG MaxLbaCount,_Inout_ PDEVICE_DATA_SET_RANGE DataSetRange)3047 ConvertDataSetRangeToUnmapBlockDescr(
3048     _In_    PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
3049     _In_    PUNMAP_BLOCK_DESCRIPTOR      BlockDescr,
3050     _Inout_ PULONG                       CurrentBlockDescrIndex,
3051     _In_    ULONG                        MaxBlockDescrIndex,
3052     _Inout_ PULONGLONG                   CurrentLbaCount,
3053     _In_    ULONGLONG                    MaxLbaCount,
3054     _Inout_ PDEVICE_DATA_SET_RANGE       DataSetRange
3055     )
3056 /*++
3057 
3058 Routine Description:
3059 
3060     Convert DEVICE_DATA_SET_RANGE entry to be UNMAP_BLOCK_DESCRIPTOR entries.
3061 
3062     As LengthInBytes field in DEVICE_DATA_SET_RANGE structure is 64 bits (bytes)
3063     and LbaCount field in UNMAP_BLOCK_DESCRIPTOR structure is 32 bits (sectors),
3064     it's possible that one DEVICE_DATA_SET_RANGE entry needs multiple UNMAP_BLOCK_DESCRIPTOR entries.
3065     We must also take the unmap granularity into consideration and split up the
3066     the given ranges so that they are aligned with the specified granularity.
3067 
3068 Arguments:
3069     All arguments must be validated by the caller.
3070 
3071     FdoExtension - The FDO extension of the device to which the unmap
3072         command that will use the resulting unmap block descriptors will be
3073         sent.
3074     BlockDescr - Pointer to a buffer that will contain the unmap block
3075         descriptors.  This buffer should be allocated by the caller and the
3076         caller should also ensure that it is large enough to contain all the
3077         requested descriptors.  Its size is implied by MaxBlockDescrIndex.
3078     CurrentBlockDescrIndex - This contains the next block descriptor index to
3079         be processed when this function returns.  This function should be called
3080         again with the same parameter to continue processing.
3081     MaxBlockDescrIndex - This is the index of the last unmap block descriptor,
3082         provided so that the function does not go off the end of BlockDescr.
3083     CurrentLbaCount - This contains the number of LBAs left to be processed
3084         when this function returns.  This function should be called again with
3085         the same parameter to continue processing.
3086     MaxLbaCount - This is the max number of LBAs that can be sent in a single
3087         unmap command.
3088     DataSetRange - This range will be modified to reflect the un-converted part.
3089         It must be valid (including being granularity-aligned) when it is first
3090         passed to this function.
3091 
3092 Return Value:
3093 
3094     Count of UNMAP_BLOCK_DESCRIPTOR entries converted.
3095 
3096     NOTE: if LengthInBytes does not reach to 0, the conversion for DEVICE_DATA_SET_RANGE entry
3097           is not completed. Further conversion is needed by calling this function again.
3098 
3099 --*/
3100 {
3101 
3102     ULONGLONG startingSector;
3103     ULONGLONG sectorCount;
3104 
3105     TracePrint((TRACE_LEVEL_INFORMATION,
3106                     TRACE_FLAG_IOCTL,
3107                     "ConvertDataSetRangeToUnmapBlockDescr (%p): Generating UNMAP Block Descriptors from DataSetRange: \
3108                      \n\t\tStartingOffset = %I64u bytes \
3109                      \n\t\tLength = %I64u bytes\n",
3110                     FdoExtension->DeviceObject,
3111                     DataSetRange->StartingOffset,
3112                     DataSetRange->LengthInBytes));
3113 
3114     while ( (DataSetRange->LengthInBytes > 0) &&
3115             (*CurrentBlockDescrIndex < MaxBlockDescrIndex) &&
3116             (*CurrentLbaCount < MaxLbaCount) ) {
3117 
3118         //
3119         // Convert the starting offset and length from bytes to blocks.
3120         //
3121         startingSector = (ULONGLONG)(DataSetRange->StartingOffset / FdoExtension->DiskGeometry.BytesPerSector);
3122         sectorCount = (DataSetRange->LengthInBytes / FdoExtension->DiskGeometry.BytesPerSector);
3123 
3124         //
3125         // Make sure the sector count isn't more than can be specified with a
3126         // single descriptor.
3127         //
3128         if (sectorCount > MAXULONG) {
3129             sectorCount = MAXULONG;
3130         }
3131 
3132         //
3133         // The max LBA count is the max number of LBAs that can be unmapped with
3134         // a single UNMAP command.  Make sure we don't exceed this value.
3135         //
3136         if ((*CurrentLbaCount + sectorCount) > MaxLbaCount) {
3137             sectorCount = MaxLbaCount - *CurrentLbaCount;
3138         }
3139 
3140         REVERSE_BYTES_QUAD(BlockDescr[*CurrentBlockDescrIndex].StartingLba, &startingSector);
3141         REVERSE_BYTES(BlockDescr[*CurrentBlockDescrIndex].LbaCount, (PULONG)&sectorCount);
3142 
3143         DataSetRange->StartingOffset += sectorCount * FdoExtension->DiskGeometry.BytesPerSector;
3144         DataSetRange->LengthInBytes -= sectorCount * FdoExtension->DiskGeometry.BytesPerSector;
3145 
3146         *CurrentBlockDescrIndex += 1;
3147         *CurrentLbaCount += (ULONG)sectorCount;
3148 
3149         TracePrint((TRACE_LEVEL_INFORMATION,
3150                     TRACE_FLAG_IOCTL,
3151                     "ConvertDataSetRangeToUnmapBlockDescr (%p): Generated UNMAP Block Descriptor: \
3152                      \n\t\t\tStartingLBA = %I64u \
3153                      \n\t\t\tLBACount = %I64u\n",
3154                     FdoExtension->DeviceObject,
3155                     startingSector,
3156                     sectorCount));
3157     }
3158 
3159     return;
3160 }
3161 
3162 
3163 NTSTATUS
DeviceProcessDsmTrimRequest(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_In_ PDEVICE_DATA_SET_RANGE DataSetRanges,_In_ ULONG DataSetRangesCount,_In_ ULONG UnmapGranularity,_In_ ULONG SrbFlags,_In_ PIRP Irp,_In_ PGUID ActivityId,_Inout_ PSCSI_REQUEST_BLOCK Srb)3164 DeviceProcessDsmTrimRequest(
3165     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
3166     _In_ PDEVICE_DATA_SET_RANGE       DataSetRanges,
3167     _In_ ULONG                        DataSetRangesCount,
3168     _In_ ULONG                        UnmapGranularity,
3169     _In_ ULONG                        SrbFlags,
3170     _In_ PIRP                         Irp,
3171     _In_ PGUID                        ActivityId,
3172     _Inout_ PSCSI_REQUEST_BLOCK       Srb
3173 )
3174 /*++
3175 
3176 Routine Description:
3177 
3178     Process TRIM request that received from upper layer.
3179 
3180 Arguments:
3181 
3182     FdoExtension
3183     DataSetRanges - this parameter must be already validated in caller.
3184     DataSetRangesCount - this parameter must be already validated in caller.
3185     UnmapGranularity - The unmap granularity in blocks.  This is used to split
3186         up the unmap command into chunks that are granularity-aligned.
3187     Srb - The SRB to use for the unmap command.  The caller must allocate it,
3188         but this function will take care of initialzing it.
3189 
3190 Return Value:
3191 
3192     status of the operation
3193 
3194 --*/
3195 {
3196     NTSTATUS                status = STATUS_SUCCESS;
3197 
3198     PUNMAP_LIST_HEADER      buffer = NULL;
3199     PUNMAP_BLOCK_DESCRIPTOR blockDescrPointer;
3200     ULONG                   bufferLength;
3201     ULONG                   maxBlockDescrCount;
3202     ULONG                   neededBlockDescrCount;
3203     ULONG                   i;
3204 
3205     BOOLEAN                 allDataSetRangeFullyConverted;
3206     BOOLEAN                 needToSendCommand;
3207     BOOLEAN                 tempDataSetRangeFullyConverted;
3208 
3209     ULONG                   dataSetRangeIndex;
3210     DEVICE_DATA_SET_RANGE   tempDataSetRange;
3211 
3212     ULONG                   blockDescrIndex;
3213     ULONGLONG               lbaCount;
3214     ULONGLONG               maxLbaCount;
3215     ULONGLONG               maxParameterListLength;
3216 
3217 
3218     UNREFERENCED_PARAMETER(UnmapGranularity);
3219     UNREFERENCED_PARAMETER(ActivityId);
3220     UNREFERENCED_PARAMETER(Irp);
3221 
3222     //
3223     // The given LBA ranges are in DEVICE_DATA_SET_RANGE format and need to be converted into UNMAP Block Descriptors.
3224     // The UNMAP command is able to carry 0xFFFF bytes (0xFFF8 in reality as there are 8 bytes of header plus n*16 bytes of Block Descriptors) of data.
3225     // The actual size will also be constrained by the Maximum LBA Count and Maximum Transfer Length.
3226     //
3227 
3228     //
3229     // 1.1 Calculate how many Block Descriptors are needed to complete this request.
3230     //
3231     neededBlockDescrCount = 0;
3232     for (i = 0; i < DataSetRangesCount; i++) {
3233         lbaCount = DataSetRanges[i].LengthInBytes / FdoExtension->DiskGeometry.BytesPerSector;
3234 
3235         //
3236         // 1.1.1 the UNMAP_BLOCK_DESCRIPTOR LbaCount is 32 bits, the max value is 0xFFFFFFFF
3237         //
3238         if (lbaCount > 0) {
3239             neededBlockDescrCount += (ULONG)((lbaCount - 1) / MAXULONG + 1);
3240         }
3241     }
3242 
3243     //
3244     // Honor Max Unmap Block Descriptor Count if it has been specified.  Otherwise,
3245     // use the maximum value that the Parameter List Length field will allow (0xFFFF).
3246     // If the count is 0xFFFFFFFF, then no maximum is specified.
3247     //
3248     if (FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapBlockDescrCount != 0 &&
3249         FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapBlockDescrCount != MAXULONG)
3250     {
3251         maxParameterListLength = (ULONGLONG)(FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapBlockDescrCount * sizeof(UNMAP_BLOCK_DESCRIPTOR))
3252                                  + sizeof(UNMAP_LIST_HEADER);
3253 
3254         //
3255         // In the SBC-3, the Max Unmap Block Descriptor Count field in the 0xB0
3256         // page is 4 bytes and the Parameter List Length in the UNMAP command is
3257         // 2 bytes, therefore it is possible that the Max Unmap Block Descriptor
3258         // Count could imply more bytes than can be specified in the Parameter
3259         // List Length field.  Adjust for that here.
3260         //
3261         maxParameterListLength = min(maxParameterListLength, MAXUSHORT);
3262     }
3263     else
3264     {
3265         maxParameterListLength = MAXUSHORT;
3266     }
3267 
3268     //
3269     // 1.2 Calculate the buffer size needed, capped by the device's limitations.
3270     //
3271     bufferLength = min(FdoExtension->PrivateFdoData->HwMaxXferLen, (ULONG)maxParameterListLength);
3272     bufferLength = min(bufferLength, (neededBlockDescrCount * sizeof(UNMAP_BLOCK_DESCRIPTOR) + sizeof(UNMAP_LIST_HEADER)));
3273 
3274     maxBlockDescrCount = (bufferLength - sizeof(UNMAP_LIST_HEADER)) / sizeof(UNMAP_BLOCK_DESCRIPTOR);
3275 
3276     if (maxBlockDescrCount == 0) {
3277         //
3278         // This shouldn't happen since we've already done validation.
3279         //
3280         TracePrint((TRACE_LEVEL_INFORMATION,
3281                                 TRACE_FLAG_IOCTL,
3282                                 "DeviceProcessDsmTrimRequest (%p): Max Block Descriptor count is Zero\n",
3283                                 FdoExtension->DeviceObject));
3284 
3285         NT_ASSERT(maxBlockDescrCount != 0);
3286         status = STATUS_DATA_ERROR;
3287         goto Exit;
3288     }
3289 
3290     //
3291     // The Maximum LBA Count is set during device initialization.
3292     //
3293     maxLbaCount = (ULONGLONG)FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapLbaCount;
3294     if (maxLbaCount == 0) {
3295         //
3296         // This shouldn't happen since we've already done validation.
3297         //
3298         TracePrint((TRACE_LEVEL_INFORMATION,
3299                                 TRACE_FLAG_IOCTL,
3300                                 "DeviceProcessDsmTrimRequest (%p): Max LBA count is Zero\n",
3301                                 FdoExtension->DeviceObject));
3302 
3303         NT_ASSERT(maxLbaCount != 0);
3304         status = STATUS_DATA_ERROR;
3305         goto Exit;
3306     }
3307 
3308     //
3309     // Finally, allocate the buffer we'll use to send the UNMAP command.
3310     //
3311 
3312 #if defined(_ARM_) || defined(_ARM64_)
3313     //
3314     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
3315     // based platforms. We are taking the conservative approach here.
3316     //
3317     bufferLength = ALIGN_UP_BY(bufferLength,KeGetRecommendedSharedDataAlignment());
3318     buffer = (PUNMAP_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, bufferLength, CLASS_TAG_LB_PROVISIONING);
3319 #else
3320     buffer = (PUNMAP_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASS_TAG_LB_PROVISIONING);
3321 #endif
3322 
3323     if (buffer == NULL) {
3324         status = STATUS_INSUFFICIENT_RESOURCES;
3325         goto Exit;
3326     }
3327 
3328     RtlZeroMemory(buffer, bufferLength);
3329 
3330     blockDescrPointer = &buffer->Descriptors[0];
3331 
3332     allDataSetRangeFullyConverted = FALSE;
3333     needToSendCommand = FALSE;
3334     tempDataSetRangeFullyConverted = TRUE;
3335     dataSetRangeIndex = 0;
3336     RtlZeroMemory(&tempDataSetRange, sizeof(tempDataSetRange));
3337 
3338     blockDescrIndex = 0;
3339     lbaCount = 0;
3340 
3341 
3342     while (!allDataSetRangeFullyConverted) {
3343 
3344         //
3345         // If the previous entry conversion completed, go on to the next one;
3346         // otherwise, continue processing the current entry.
3347         //
3348         if (tempDataSetRangeFullyConverted) {
3349             tempDataSetRange.StartingOffset = DataSetRanges[dataSetRangeIndex].StartingOffset;
3350             tempDataSetRange.LengthInBytes = DataSetRanges[dataSetRangeIndex].LengthInBytes;
3351             dataSetRangeIndex++;
3352         }
3353 
3354         ConvertDataSetRangeToUnmapBlockDescr(FdoExtension,
3355                                                 blockDescrPointer,
3356                                                 &blockDescrIndex,
3357                                                 maxBlockDescrCount,
3358                                                 &lbaCount,
3359                                                 maxLbaCount,
3360                                                 &tempDataSetRange
3361                                                 );
3362 
3363         tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
3364 
3365         allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && (dataSetRangeIndex == DataSetRangesCount);
3366 
3367         //
3368         // Send the UNMAP command when the buffer is full or when all input entries are converted.
3369         //
3370         if ((blockDescrIndex == maxBlockDescrCount) ||         // Buffer full or block descriptor count reached
3371             (lbaCount == maxLbaCount) ||                       // Block LBA count reached
3372              allDataSetRangeFullyConverted) {                   // All DataSetRanges have been converted
3373 
3374             USHORT transferSize;
3375             USHORT tempSize;
3376             PCDB   cdb;
3377 
3378             //
3379             // Get the transfer size, including the header.
3380             //
3381             transferSize = (USHORT)(blockDescrIndex * sizeof(UNMAP_BLOCK_DESCRIPTOR) + sizeof(UNMAP_LIST_HEADER));
3382             if (transferSize > bufferLength)
3383             {
3384                 //
3385                 // This should never happen.
3386                 //
3387                 NT_ASSERT(transferSize <= bufferLength);
3388                 status = STATUS_BUFFER_TOO_SMALL;
3389                 break;
3390             }
3391 
3392             tempSize = transferSize - (USHORT)FIELD_OFFSET(UNMAP_LIST_HEADER, BlockDescrDataLength);
3393             REVERSE_BYTES_SHORT(buffer->DataLength, &tempSize);
3394             tempSize = transferSize - (USHORT)FIELD_OFFSET(UNMAP_LIST_HEADER, Descriptors[0]);
3395             REVERSE_BYTES_SHORT(buffer->BlockDescrDataLength, &tempSize);
3396 
3397             //
3398             // Initialize the SRB.
3399             //
3400             if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
3401                 status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
3402                                                        STORAGE_ADDRESS_TYPE_BTL8,
3403                                                        CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
3404                                                        1,
3405                                                        SrbExDataTypeScsiCdb16);
3406                 if (NT_SUCCESS(status)) {
3407                     ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
3408                 } else {
3409                     //
3410                     // Should not occur.
3411                     //
3412                     NT_ASSERT(FALSE);
3413                     break;
3414                 }
3415 
3416             } else {
3417                 RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
3418                 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
3419                 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3420             }
3421 
3422             //
3423             // Prepare the Srb
3424             //
3425             SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
3426             SrbSetRequestTag(Srb, SP_UNTAGGED);
3427             SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
3428 
3429             //
3430             // Set the SrbFlags to indicate that it's a data-out operation.
3431             // Also set any passed-in SrbFlags.
3432             //
3433             SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
3434             SrbClearSrbFlags(Srb, SRB_FLAGS_DATA_IN);
3435             SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_OUT);
3436             SrbSetSrbFlags(Srb, SrbFlags);
3437 
3438             SrbSetCdbLength(Srb, 10);
3439 
3440             cdb = SrbGetCdb(Srb);
3441             cdb->UNMAP.OperationCode = SCSIOP_UNMAP;
3442             cdb->UNMAP.Anchor = 0;
3443             cdb->UNMAP.GroupNumber = 0;
3444             cdb->UNMAP.AllocationLength[0] = (UCHAR)(transferSize >> 8);
3445             cdb->UNMAP.AllocationLength[1] = (UCHAR)transferSize;
3446 
3447             status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
3448                                              Srb,
3449                                              buffer,
3450                                              transferSize,
3451                                              TRUE);
3452 
3453             TracePrint((TRACE_LEVEL_INFORMATION,
3454                         TRACE_FLAG_IOCTL,
3455                         "DeviceProcessDsmTrimRequest (%p): UNMAP command issued. Returned NTSTATUS: %!STATUS!.\n",
3456                         FdoExtension->DeviceObject,
3457                         status
3458                         ));
3459 
3460             //
3461             // Clear the buffer so we can re-use it.
3462             //
3463             blockDescrIndex = 0;
3464             lbaCount = 0;
3465             RtlZeroMemory(buffer, bufferLength);
3466         }
3467     }
3468 
3469 Exit:
3470 
3471     FREE_POOL(buffer);
3472 
3473     return status;
3474 }
3475 
ClasspDeviceTrimProcess(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_In_ PGUID ActivityId,_Inout_ PSCSI_REQUEST_BLOCK Srb)3476 NTSTATUS ClasspDeviceTrimProcess(
3477     _In_ PDEVICE_OBJECT DeviceObject,
3478     _In_ PIRP Irp,
3479     _In_ PGUID ActivityId,
3480     _Inout_ PSCSI_REQUEST_BLOCK Srb
3481     )
3482 /*
3483     This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Trim.
3484     At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
3485     If it's not supported, UNMAP (with anchor attribute set) will be sent down to process the request.
3486 */
3487 {
3488     NTSTATUS    status = STATUS_SUCCESS;
3489 
3490     PCOMMON_DEVICE_EXTENSION     commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
3491     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
3492 
3493     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
3494 
3495     PDEVICE_DATA_SET_RANGE      dataSetRanges;
3496     ULONG                       dataSetRangesCount;
3497     DEVICE_DATA_SET_RANGE       entireDataSetRange = {0};
3498     ULONG                       i;
3499     ULONGLONG                   granularityAlignmentInBytes;
3500     ULONG                       granularityInBlocks;
3501     ULONG                       srbFlags = 0;
3502 
3503     CLASS_VPD_B0_DATA           blockLimitsData;
3504     ULONG                       generationCount;
3505 
3506 
3507     if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
3508          (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
3509          (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess == Supported) ) {
3510         // if it's not disk, forward the request to lower layer,
3511         // if the IOCTL is supported by lower stack, forward it down.
3512         IoCopyCurrentIrpStackLocationToNext(Irp);
3513 
3514         TracePrint((TRACE_LEVEL_INFORMATION,
3515                     TRACE_FLAG_GENERAL,
3516                     "ClasspDeviceTrimProcess (%p): Lower layer supports Trim DSM IOCTL, forwarding IOCTL.\n",
3517                     DeviceObject));
3518 
3519         ClassReleaseRemoveLock(DeviceObject, Irp);
3520         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
3521         return status;
3522     }
3523 
3524     //
3525     // Request validation.
3526     // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
3527     //
3528 
3529     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
3530         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
3531         status = STATUS_INVALID_LEVEL;
3532         goto Exit;
3533     }
3534 
3535 
3536     //
3537     // If the caller has not set the "entire dataset range" flag then at least
3538     // one dataset range should be specified.  However, if the caller *has* set
3539     // the flag, then there should not be any dataset ranges specified.
3540     //
3541     if ((!TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) &&
3542          (dsmAttributes->DataSetRangesOffset == 0 ||
3543           dsmAttributes->DataSetRangesLength == 0)) ||
3544         (TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) &&
3545          (dsmAttributes->DataSetRangesOffset != 0 ||
3546           dsmAttributes->DataSetRangesLength != 0))) {
3547 
3548         status = STATUS_INVALID_PARAMETER;
3549         goto Exit;
3550     }
3551 
3552     //
3553     // note that 'Supported' case has been handled at the beginning of this function.
3554     //
3555     switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess) {
3556         case SupportUnknown: {
3557             // send down request and wait for the request to complete.
3558             status = ClassForwardIrpSynchronous(commonExtension, Irp);
3559 
3560             TracePrint((TRACE_LEVEL_INFORMATION,
3561                     TRACE_FLAG_GENERAL,
3562                     "ClasspDeviceTrimProcess (%p): Trim DSM IOCTL support unknown.  Forwarded IOCTL and received NTSTATUS %!STATUS!.\n",
3563                     DeviceObject,
3564                     status));
3565 
3566             if (ClasspLowerLayerNotSupport(status)) {
3567                 // case 1: the request is not supported by lower layer, sends down command
3568                 // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
3569                 // In this case we'll just fall through to the NotSupported case so that we can handle it ourselves.
3570 
3571                 //
3572                 // VPD pages 0xB2 and 0xB0 should have been cached in Start Device phase - ClassPnpStartDevice.
3573                 // 0xB2 page: fdoExtension->FunctionSupportInfo->LBProvisioningData;
3574                 // 0xB0 page: fdoExtension->FunctionSupportInfo->BlockLimitsData
3575                 //
3576                 if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE) {
3577                     NT_ASSERT(fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus != -1);
3578                 }
3579 
3580                 if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE) {
3581                     NT_ASSERT(fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus != -1);
3582                 }
3583 
3584             } else {
3585 
3586                 // case 2: the request is supported and it completes successfully
3587                 // case 3: the request is supported by lower stack but other failure status is returned.
3588                 // from now on, the same request will be send down to lower stack directly.
3589                 fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess = Supported;
3590                 goto Exit;
3591             }
3592         }
3593 
3594         case NotSupported: {
3595 
3596             // send UNMAP command if it is supported. don't need to check 'status' value.
3597             if (ClasspSupportsUnmap(fdoExtension->FunctionSupportInfo))
3598             {
3599                 //
3600                 // Make sure that we know the bytes per sector (logical block) as it's
3601                 // necessary for calculations involving granularity and alignment.
3602                 //
3603                 if (fdoExtension->DiskGeometry.BytesPerSector == 0) {
3604                     status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
3605                     if(!NT_SUCCESS(status) || fdoExtension->DiskGeometry.BytesPerSector == 0) {
3606                         status = STATUS_INVALID_DEVICE_REQUEST;
3607                         goto Exit;
3608                     }
3609                 }
3610 
3611                 //
3612                 // Take a snapshot of the block limits data since it can change.
3613                 // It's acceptable if the block limits data is outdated since
3614                 // there isn't a hard requirement on the unmap granularity.
3615                 //
3616                 ClasspBlockLimitsDataSnapshot(fdoExtension,
3617                                               FALSE,
3618                                               &blockLimitsData,
3619                                               &generationCount);
3620 
3621                 //
3622                 // Check to see if the Optimal Unmap Granularity and Unmap Granularity
3623                 // Alignment have been specified.  If not, default the granularity to
3624                 // one block and the alignment to zero.
3625                 //
3626                 if (blockLimitsData.OptimalUnmapGranularity != 0)
3627                 {
3628                     granularityInBlocks = blockLimitsData.OptimalUnmapGranularity;
3629                 }
3630                 else
3631                 {
3632                     granularityInBlocks = 1;
3633 
3634                     TracePrint((TRACE_LEVEL_INFORMATION,
3635                                 TRACE_FLAG_GENERAL,
3636                                 "ClasspDeviceTrimProcess (%p): Optimal Unmap Granularity not provided, defaulted to 1.\n",
3637                                 DeviceObject));
3638                 }
3639 
3640                 if (blockLimitsData.UGAVALID == TRUE)
3641                 {
3642                     granularityAlignmentInBytes = (ULONGLONG)blockLimitsData.UnmapGranularityAlignment * fdoExtension->DiskGeometry.BytesPerSector;
3643                 }
3644                 else
3645                 {
3646                     granularityAlignmentInBytes = 0;
3647 
3648                     TracePrint((TRACE_LEVEL_INFORMATION,
3649                                 TRACE_FLAG_GENERAL,
3650                                 "ClasspDeviceTrimProcess (%p): Unmap Granularity Alignment not provided, defaulted to 0.\n",
3651                                 DeviceObject));
3652                 }
3653 
3654                 if (TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE))
3655                 {
3656                     //
3657                     // The caller wants to UNMAP the entire disk so we need to build a single
3658                     // dataset range that represents the entire disk.
3659                     //
3660                     entireDataSetRange.StartingOffset = granularityAlignmentInBytes;
3661                     entireDataSetRange.LengthInBytes = (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart - (ULONGLONG)entireDataSetRange.StartingOffset;
3662 
3663                     dataSetRanges = &entireDataSetRange;
3664                     dataSetRangesCount = 1;
3665                 }
3666                 else
3667                 {
3668 
3669                     dataSetRanges = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset);
3670                     dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
3671 
3672                     //
3673                     // Validate the data ranges.  Make sure the range is block-aligned,
3674                     // falls in a valid portion of the disk, and is non-zero.
3675                     //
3676                     for (i = 0; i < dataSetRangesCount; i++)
3677                     {
3678                         if ((dataSetRanges[i].StartingOffset % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
3679                             (dataSetRanges[i].LengthInBytes % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
3680                             (dataSetRanges[i].StartingOffset < (LONGLONG)granularityAlignmentInBytes) ||
3681                             (dataSetRanges[i].LengthInBytes == 0) ||
3682                             ((ULONGLONG)dataSetRanges[i].StartingOffset + dataSetRanges[i].LengthInBytes > (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart))
3683                         {
3684                             TracePrint((TRACE_LEVEL_ERROR,
3685                                         TRACE_FLAG_IOCTL,
3686                                         "ClasspDeviceTrimProcess (%p): Invalid dataset range.  StartingOffset = %I64x, LengthInBytes = %I64x\n",
3687                                         DeviceObject,
3688                                         dataSetRanges[i].StartingOffset,
3689                                         dataSetRanges[i].LengthInBytes));
3690 
3691                             status = STATUS_INVALID_PARAMETER;
3692                             goto Exit;
3693                         }
3694                     }
3695                 }
3696 
3697 
3698                 if (!TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED))
3699                 {
3700                     {
3701                         //
3702                         // For security reasons, file-level TRIM must be forwarded on only
3703                         // if reading the unmapped blocks' contents will return back zeros.
3704                         // This is because if LBPRZ bit is not set, it indicates that a read
3705                         // of unmapped blocks may return "any" data thus potentially leaking
3706                         // in data (into the read buffer) from other blocks.
3707                         //
3708                         if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning &&
3709                             !fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ) {
3710 
3711                             TracePrint((TRACE_LEVEL_ERROR,
3712                                         TRACE_FLAG_IOCTL,
3713                                         "ClasspDeviceTrimProcess (%p): Device does not support file level TRIM.\n",
3714                                         DeviceObject));
3715 
3716                             status = STATUS_TRIM_READ_ZERO_NOT_SUPPORTED;
3717                             goto Exit;
3718                         }
3719                     }
3720                 }
3721 
3722                 // process DSM IOCTL
3723                 status = DeviceProcessDsmTrimRequest(fdoExtension,
3724                                                      dataSetRanges,
3725                                                      dataSetRangesCount,
3726                                                      granularityInBlocks,
3727                                                      srbFlags,
3728                                                      Irp,
3729                                                      ActivityId,
3730                                                      Srb);
3731             } else {
3732                 // DSM IOCTL should be completed as not supported
3733 
3734                 TracePrint((TRACE_LEVEL_ERROR,
3735                             TRACE_FLAG_IOCTL,
3736                             "ClasspDeviceTrimProcess (%p): Device does not support UNMAP.\n",
3737                             DeviceObject));
3738 
3739                 status = STATUS_NOT_SUPPORTED;
3740             }
3741 
3742             // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
3743             fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess = NotSupported;
3744 
3745             break;
3746         }
3747 
3748         case Supported: {
3749             NT_ASSERT(FALSE); // this case is handled at the begining of the function.
3750             break;
3751         }
3752 
3753     }   // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess)
3754 
3755 Exit:
3756 
3757     //
3758     // Set the size and status in IRP
3759     //
3760     Irp->IoStatus.Information = 0;
3761     Irp->IoStatus.Status = status;
3762 
3763 
3764 
3765     ClassReleaseRemoveLock(DeviceObject, Irp);
3766     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
3767 
3768     return status;
3769 }
3770 
3771 NTSTATUS
GetLBAStatus(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_In_ PSCSI_REQUEST_BLOCK Srb,_In_ ULONGLONG StartingLBA,_Inout_ PLBA_STATUS_LIST_HEADER LBAStatusHeader,_In_ ULONG LBAStatusSize,_In_ BOOLEAN ConsolidateableBlocksOnly)3772 GetLBAStatus(
3773     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
3774     _In_ PSCSI_REQUEST_BLOCK          Srb,
3775     _In_ ULONGLONG                    StartingLBA,
3776     _Inout_ PLBA_STATUS_LIST_HEADER   LBAStatusHeader,
3777     _In_ ULONG                        LBAStatusSize,
3778     _In_ BOOLEAN                      ConsolidateableBlocksOnly
3779     )
3780 /*++
3781 
3782 Routine Description:
3783 
3784     Send down a Get LBA Status command for the given range.
3785 
3786 Arguments:
3787     FdoExtension: The FDO extension of the device to which Get LBA Status will
3788         be sent.
3789     Srb: This should be allocated and initialized before it's passed in.  It
3790         will be used for the Get LBA Status command.
3791     StartingLBA: The LBA that is at the beginning of the requested range.
3792     LBAStatusHeader: Caller-allocated output buffer.
3793     LBASTatusSize: Size of the caller-allocated output buffer.
3794 
3795 Return Value:
3796 
3797     Status of the operation.
3798 
3799 --*/
3800 {
3801     NTSTATUS                status = STATUS_SUCCESS;
3802     PCDB                    cdb;
3803 
3804     if (LBAStatusHeader == NULL || LBAStatusSize == 0)
3805     {
3806         return STATUS_INVALID_PARAMETER;
3807     }
3808 
3809     //
3810     // Build and send down the Get LBA Status command.
3811     //
3812     SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
3813     SrbSetRequestTag(Srb, SP_UNTAGGED);
3814     SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
3815     SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
3816     SrbSetCdbLength(Srb, sizeof(cdb->GET_LBA_STATUS));
3817 
3818 
3819     cdb = SrbGetCdb(Srb);
3820     cdb->GET_LBA_STATUS.OperationCode = SCSIOP_GET_LBA_STATUS;
3821     cdb->GET_LBA_STATUS.ServiceAction = SERVICE_ACTION_GET_LBA_STATUS;
3822     REVERSE_BYTES_QUAD(&(cdb->GET_LBA_STATUS.StartingLBA), &StartingLBA);
3823     REVERSE_BYTES(&(cdb->GET_LBA_STATUS.AllocationLength), &LBAStatusSize);
3824 
3825     TracePrint((TRACE_LEVEL_INFORMATION,
3826                 TRACE_FLAG_IOCTL,
3827                 "GetLBAStatus (%p): sending command with StartingLBA = 0x%I64x, AllocationLength = 0x%I64x, ConsolidateableBlocksOnly = %u\n",
3828                 FdoExtension->DeviceObject,
3829                 StartingLBA,
3830                 LBAStatusSize,
3831                 ConsolidateableBlocksOnly));
3832 
3833     status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
3834                                         Srb,
3835                                         LBAStatusHeader,
3836                                         LBAStatusSize,
3837                                         FALSE);
3838 
3839     //
3840     // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
3841     // buffer was larger than necessary.
3842     //
3843     if (status == STATUS_DATA_OVERRUN &&
3844         SrbGetDataTransferLength(Srb) < LBAStatusSize)
3845     {
3846         status = STATUS_SUCCESS;
3847     }
3848 
3849     // log command.
3850     TracePrint((TRACE_LEVEL_INFORMATION,
3851                 TRACE_FLAG_IOCTL,
3852                 "GetLBAStatus (%p): command returned NT Status: %!STATUS!\n",
3853                 FdoExtension->DeviceObject,
3854                 status
3855                 ));
3856 
3857     return status;
3858 }
3859 
3860 
ClasspDeviceGetLBAStatus(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)3861 NTSTATUS ClasspDeviceGetLBAStatus(
3862     _In_ PDEVICE_OBJECT DeviceObject,
3863     _Inout_ PIRP Irp,
3864     _Inout_ PSCSI_REQUEST_BLOCK Srb
3865     )
3866 /*
3867 Routine Description:
3868 
3869     This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Allocation.
3870 
3871     1. This function will only handle the first dataset range.
3872     2. This function will not handle dataset ranges whose LengthInBytes is greater than:
3873         ((MAXULONG - sizeof(LBA_STATUS_LIST_HEADER)) / sizeof(LBA_STATUS_DESCRIPTOR)) * BytesPerSlab
3874 
3875     The input buffer should consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES followed
3876     in memory by a single DEVICE_DATA_SET_RANGE that specifies the requested range
3877     of slabs for which mapping status is desired.
3878 
3879     The output buffer will consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT
3880     followed in memory by a single DEVICE_DATA_SET_LB_PROVISIONING_STATE that
3881     contains a bitmap that represents the mapped status of the slabs in the requested
3882     range.  Note that the number of slabs returned may be less than the number
3883     requested.
3884 
3885     Thus function will automatically re-align the given range offset if it was
3886     not slab-aligned.  The delta between the given range offset and the properly
3887     aligned offset will be given in returned DEVICE_DATA_SET_LB_PROVISIONING_STATE.
3888 
3889 Arguments:
3890     DeviceObject: The FDO of the device to which Get LBA Status will be sent.
3891     Irp: The IRP for the request.  This function will read the input buffer and
3892         write to the output buffer at the current IRP stack location.
3893     Srb: This should be allocated and initialized before it's passed in.  It
3894         will be used for the Get LBA Status command.
3895 
3896 Return Value:
3897 
3898     STATUS_INVALID_PARAMETER: May be returned under the following conditions:
3899         - If the requested range was too large.  The caller should try again with a
3900             smaller range.  See above for how to calculate the maximum range.
3901         - If the given starting offset was not within the valid range of the device.
3902     STATUS_NOT_SUPPORTED: The storage did not report some information critical to
3903         the execution of this function (e.g. Optimal Unmap Granularity).
3904     STATUS_BUFFER_TOO_SMALL: The output buffer is not large enough to hold the max
3905         data that could be returned from this function.  If the output buffer is
3906         at least the size of a ULONG, we will write the required output buffer size
3907         to the first ULONG bytes of the output buffer.
3908     STATUS_UNSUCCESSFUL: The Get LBA Status command succeeded but did not
3909         return data as expected.
3910 --*/
3911 {
3912     PFUNCTIONAL_DEVICE_EXTENSION               fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
3913     PIO_STACK_LOCATION                         irpStack = IoGetCurrentIrpStackLocation(Irp);
3914     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES         dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer;
3915     PDEVICE_DATA_SET_RANGE                     dataSetRanges = NULL;
3916     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT  dsmOutput = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT)Irp->AssociatedIrp.SystemBuffer;
3917     ULONG                                      dsmOutputLength;
3918     NTSTATUS                                   finalStatus;
3919     NTSTATUS                                   getLBAWorkerStatus;
3920     ULONG                                      retryCount;
3921     ULONG                                      retryCountMax;
3922     CLASS_VPD_B0_DATA                          blockLimitsData;
3923     ULONG                                      generationCount1;
3924     ULONG                                      generationCount2;
3925     BOOLEAN                                    blockLimitsDataMayHaveChanged;
3926     ULONG_PTR                                  information = 0;
3927     LONGLONG                                   startingOffset;
3928     ULONGLONG                                  lengthInBytes;
3929     BOOLEAN                                    consolidateableBlocksOnly = FALSE;
3930     ULONG                                      outputVersion;
3931 
3932     //
3933     // Basic parameter validation.
3934     // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
3935     //
3936     if (dsmOutput == NULL ||
3937         dsmAttributes == NULL)
3938     {
3939         finalStatus = STATUS_INVALID_PARAMETER;
3940         goto Exit;
3941     }
3942 
3943     if (TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE)) {
3944         //
3945         // The caller wants the mapping status of the entire disk.
3946         //
3947         ULONG unmapGranularityAlignment = 0;
3948         if (fdoExtension->FunctionSupportInfo->BlockLimitsData.UGAVALID) {
3949             unmapGranularityAlignment = fdoExtension->FunctionSupportInfo->BlockLimitsData.UnmapGranularityAlignment;
3950         }
3951         startingOffset = unmapGranularityAlignment;
3952         lengthInBytes = (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart - (ULONGLONG)startingOffset;
3953     } else {
3954         if (dsmAttributes->DataSetRangesOffset == 0 ||
3955             dsmAttributes->DataSetRangesLength == 0) {
3956             finalStatus = STATUS_INVALID_PARAMETER;
3957             goto Exit;
3958         }
3959 
3960         //
3961         // We only service the first dataset range specified.
3962         //
3963         dataSetRanges = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset);
3964         startingOffset = dataSetRanges[0].StartingOffset;
3965         lengthInBytes = dataSetRanges[0].LengthInBytes;
3966     }
3967 
3968 
3969     //
3970     // See if the sender is requesting a specific version of the output data
3971     // structure.  Othwerwise, default to V1.
3972     //
3973     outputVersion = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1;
3974 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
3975     if ((dsmAttributes->ParameterBlockOffset >= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) &&
3976         (dsmAttributes->ParameterBlockLength >= sizeof(DEVICE_DATA_SET_LBP_STATE_PARAMETERS))) {
3977         PDEVICE_DATA_SET_LBP_STATE_PARAMETERS parameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
3978         if ((parameters->Version == DEVICE_DATA_SET_LBP_STATE_PARAMETERS_VERSION_V1) &&
3979             (parameters->Size >= sizeof(DEVICE_DATA_SET_LBP_STATE_PARAMETERS))) {
3980 
3981             outputVersion = parameters->OutputVersion;
3982 
3983             if ((outputVersion != DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1) &&
3984                 (outputVersion != DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2)) {
3985                 finalStatus = STATUS_INVALID_PARAMETER;
3986                 goto Exit;
3987             }
3988         }
3989     }
3990 #endif
3991 
3992     //
3993     // Take a snapshot of the block limits data for the worker function to use.
3994     // We need to fail the request if we fail to get updated block limits data
3995     // since we need an accurate Optimal Unmap Granularity value to properly
3996     // convert the returned mapping descriptors into a bitmap.
3997     //
3998     finalStatus = ClasspBlockLimitsDataSnapshot(fdoExtension,
3999                                                 TRUE,
4000                                                 &blockLimitsData,
4001                                                 &generationCount1);
4002 
4003     if (!NT_SUCCESS(finalStatus)) {
4004         information = 0;
4005         goto Exit;
4006     }
4007 
4008     if (dsmAttributes->Flags & DEVICE_DSM_FLAG_ALLOCATION_CONSOLIDATEABLE_ONLY) {
4009         consolidateableBlocksOnly = TRUE;
4010     }
4011 
4012     //
4013     // The retry logic is to handle the case when block limits data changes during rare occasions
4014     // (e.g. diff-VHD fork or merge).
4015     //
4016     retryCountMax = GET_LBA_STATUS_RETRY_COUNT_MAX;
4017     for (retryCount = 0; retryCount < retryCountMax; retryCount++) {
4018 
4019         dsmOutputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
4020         getLBAWorkerStatus = ClasspDeviceGetLBAStatusWorker(DeviceObject,
4021                                                             &blockLimitsData,
4022                                                             startingOffset,
4023                                                             lengthInBytes,
4024                                                             dsmOutput,
4025                                                             &dsmOutputLength,
4026                                                             Srb,
4027                                                             consolidateableBlocksOnly,
4028                                                             outputVersion,
4029                                                             &blockLimitsDataMayHaveChanged);
4030 
4031         if (!NT_SUCCESS(getLBAWorkerStatus) && !blockLimitsDataMayHaveChanged) {
4032             information = 0;
4033             finalStatus = getLBAWorkerStatus;
4034             break;
4035         }
4036 
4037         //
4038         // Again, we need to fail the request if we fail to get updated block
4039         // limits data since we need an accurate Optimal Unmap Granularity value.
4040         //
4041         finalStatus = ClasspBlockLimitsDataSnapshot(fdoExtension,
4042                                                     TRUE,
4043                                                     &blockLimitsData,
4044                                                     &generationCount2);
4045         if (!NT_SUCCESS(finalStatus)) {
4046             information = 0;
4047             goto Exit;
4048         }
4049 
4050         if (generationCount1 == generationCount2) {
4051             //
4052             // Block limits data stays the same during the call to ClasspDeviceGetLBAStatusWorker()
4053             // The result from ClasspDeviceGetLBAStatusWorker() is valid.
4054             //
4055             finalStatus = getLBAWorkerStatus;
4056             if (NT_SUCCESS(finalStatus)) {
4057                 information = dsmOutputLength;
4058             }
4059             break;
4060         }
4061 
4062         //
4063         // Try again with the latest block limits data
4064         //
4065         generationCount1 = generationCount2;
4066         information = 0;
4067         finalStatus = STATUS_DEVICE_DATA_ERROR;
4068     }
4069 
4070 Exit:
4071     Irp->IoStatus.Information = information;
4072     Irp->IoStatus.Status = finalStatus;
4073     ClassReleaseRemoveLock(DeviceObject, Irp);
4074     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4075     return finalStatus;
4076 }
4077 
4078 NTSTATUS
ClasspDeviceGetLBAStatusWorker(_In_ PDEVICE_OBJECT DeviceObject,_In_ PCLASS_VPD_B0_DATA BlockLimitsData,_In_ ULONGLONG StartingOffset,_In_ ULONGLONG LengthInBytes,_Out_ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT DsmOutput,_Inout_ PULONG DsmOutputLength,_Inout_ PSCSI_REQUEST_BLOCK Srb,_In_ BOOLEAN ConsolidateableBlocksOnly,_In_ ULONG OutputVersion,_Out_ PBOOLEAN BlockLimitsDataMayHaveChanged)4079 ClasspDeviceGetLBAStatusWorker(
4080     _In_ PDEVICE_OBJECT DeviceObject,
4081     _In_ PCLASS_VPD_B0_DATA BlockLimitsData,
4082     _In_ ULONGLONG StartingOffset,
4083     _In_ ULONGLONG LengthInBytes,
4084     _Out_ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT DsmOutput,
4085     _Inout_ PULONG DsmOutputLength,
4086     _Inout_ PSCSI_REQUEST_BLOCK Srb,
4087     _In_ BOOLEAN ConsolidateableBlocksOnly,
4088     _In_ ULONG OutputVersion,
4089     _Out_ PBOOLEAN BlockLimitsDataMayHaveChanged
4090     )
4091 /*
4092 Routine Description:
4093 
4094     This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Allocation.
4095 
4096     1. This function will only handle the first dataset range.
4097     2. This function will not handle dataset ranges whose LengthInBytes is greater than:
4098         ((MAXULONG - sizeof(LBA_STATUS_LIST_HEADER)) / sizeof(LBA_STATUS_DESCRIPTOR)) * BytesPerSlab
4099 
4100     The input buffer should consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES followed
4101     in memory by a single DEVICE_DATA_SET_RANGE that specifies the requested range
4102     of slabs for which mapping status is desired.
4103 
4104     The output buffer will consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT
4105     followed in memory by a single DEVICE_DATA_SET_LB_PROVISIONING_STATE that
4106     contains a bitmap that represents the mapped status of the slabs in the requested
4107     range.  Note that the number of slabs returned may be less than the number
4108     requested.
4109 
4110     Thus function will automatically re-align the given range offset if it was
4111     not slab-aligned.  The delta between the given range offset and the properly
4112     aligned offset will be given in returned DEVICE_DATA_SET_LB_PROVISIONING_STATE.
4113 
4114 Arguments:
4115     DeviceObject: The FDO of the device to which Get LBA Status will be sent.
4116     BlockLimitsData: Block limits data of the device
4117     StartingOffset: Starting byte offset of byte range to query LBA status (must be sector aligned)
4118     LengthInBytes: Length of byte range to query LBA status (multiple of sector size)
4119     DsmOutput: Output data buffer
4120     DsmOutputLength: output data buffer size.  It will be updated with actual bytes used.
4121     Srb: This should be allocated and initialized before it's passed in.  It
4122         will be used for the Get LBA Status command.
4123     ConsolidateableBlocksOnly: Only blocks that are eligible for consolidation
4124         should be returned.
4125     OutputVersion: The version of the DEVICE_DATA_SET_LB_PROVISIONING_STATE
4126         structure to return.  This should be one of:
4127             - DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1
4128             - DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2
4129     BlockLimitsDataMayHaveChanged: if this function fails, this flag indicates
4130         if the failure can be caused by changes in device's block limit data.
4131 
4132 Return Value:
4133 
4134     STATUS_INVALID_PARAMETER: May be returned under the following conditions:
4135         - If the requested range was too large.  The caller should try again with a
4136             smaller range.  See above for how to calculate the maximum range.
4137         - If the given starting offset was not within the valid range of the device.
4138     STATUS_NOT_SUPPORTED: The storage did not report some information critical to
4139         the execution of this function (e.g. Optimal Unmap Granularity).
4140     STATUS_BUFFER_TOO_SMALL: The output buffer is not large enough to hold the max
4141         data that could be returned from this function.  If the output buffer is
4142         at least the size of a ULONG, we will write the required output buffer size
4143         to the first ULONG bytes of the output buffer.
4144     STATUS_DEVICE_DATA_ERROR: The Get LBA Status command succeeded but did not
4145         return data as expected.
4146 --*/
4147 {
4148     NTSTATUS    status = STATUS_SUCCESS;
4149 
4150     PFUNCTIONAL_DEVICE_EXTENSION               fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
4151 
4152     PDEVICE_DATA_SET_LB_PROVISIONING_STATE     lbpState;
4153     ULONG                                      bitMapGranularityInBits = FIELD_SIZE(DEVICE_DATA_SET_LB_PROVISIONING_STATE,SlabAllocationBitMap[0]) * 8;
4154     ULONG                                      requiredOutputLength;
4155     ULONG                                      outputLength = *DsmOutputLength;
4156 
4157     ULONG                                      blocksPerSlab;
4158     ULONGLONG                                  bytesPerSlab;
4159     ULONGLONG                                  alignmentInBytes = 0;
4160     ULONG                                      alignmentInBlocks = 0;
4161     ULONG                                      maxBufferSize;
4162     ULONG                                      maxSlabs;
4163     ULONGLONG                                  requestedSlabs; // Total number of slabs requested by the caller.
4164     ULONGLONG                                  startingLBA;
4165     ULONGLONG                                  startingOffsetDelta;
4166     ULONG                                      totalProcessedSlabs = 0; // Total number of slabs we processed.
4167     ULONGLONG                                  slabsPerCommand; // Number of slabs we can ask for in one Get LBA Status command.
4168     BOOLEAN                                    doneProcessing = FALSE; // Indicates we should break out of the Get LBA Status loop.
4169 
4170     ULONG                                      lbaStatusSize;
4171     PLBA_STATUS_LIST_HEADER                    lbaStatusListHeader = NULL;
4172 
4173     //
4174     // This function can fail if the block limits data on the device changes.
4175     // This flag tells the caller if it should retry with a newer block limits data
4176     //
4177     *BlockLimitsDataMayHaveChanged = FALSE;
4178 
4179     //
4180     // Make sure we're running at PASSIVE_LEVEL
4181     //
4182     if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
4183     {
4184         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
4185         status = STATUS_INVALID_LEVEL;
4186         goto Exit;
4187     }
4188 
4189     //
4190     // Don't send down a Get LBA Status command if UNMAP isn't supported.
4191     //
4192     if (!fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU)
4193     {
4194         return STATUS_NOT_SUPPORTED;
4195         goto Exit;
4196     }
4197 
4198     //
4199     // Make sure we have a non-zero value for the number of bytes per block.
4200     // Otherwise we will end up dividing by zero later on.
4201     //
4202     if (fdoExtension->DiskGeometry.BytesPerSector == 0)
4203     {
4204         status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
4205         if(!NT_SUCCESS(status) || fdoExtension->DiskGeometry.BytesPerSector == 0)
4206         {
4207             status = STATUS_INVALID_DEVICE_REQUEST;
4208             goto Exit;
4209         }
4210     }
4211 
4212     //
4213     // We only service the first dataset range specified.
4214     //
4215     if (BlockLimitsData->UGAVALID == TRUE) {
4216         alignmentInBlocks = BlockLimitsData->UnmapGranularityAlignment;
4217         alignmentInBytes = (ULONGLONG)alignmentInBlocks * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
4218     }
4219 
4220     //
4221     // Make sure the specified range is valid.  The Unmap Granularity Alignment
4222     // defines a region at the beginning of the disk that cannot be
4223     // mapped/unmapped so the specified range should not include any part of that
4224     // region.
4225     //
4226     if (LengthInBytes == 0 ||
4227         StartingOffset < alignmentInBytes ||
4228         StartingOffset + LengthInBytes > (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart)
4229     {
4230         TracePrint((TRACE_LEVEL_ERROR,
4231                     TRACE_FLAG_IOCTL,
4232                     "ClasspDeviceGetLBAStatusWorker (%p): Invalid range, length is %I64u bytes, starting offset is %I64u bytes, Unmap alignment is %I64u bytes, and disk size is %I64u bytes\n",
4233                     DeviceObject,
4234                     LengthInBytes,
4235                     StartingOffset,
4236                     alignmentInBytes,
4237                     (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart));
4238 
4239         status = STATUS_INVALID_PARAMETER;
4240         goto Exit;
4241     }
4242 
4243     //
4244     // Calculate the number of bytes per slab so that we can convert (and
4245     // possibly align) the given offset (given in bytes) to slabs.
4246     //
4247     blocksPerSlab = BlockLimitsData->OptimalUnmapGranularity;
4248     bytesPerSlab = (ULONGLONG)blocksPerSlab * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
4249 
4250     //
4251     // If the starting offset is not slab-aligned, we need to adjust it to
4252     // be aligned with the next highest slab.  We also need to save the delta
4253     // to return to the user later.
4254     //
4255     if (((StartingOffset - alignmentInBytes) % bytesPerSlab) != 0)
4256     {
4257         startingLBA = (((StartingOffset - alignmentInBytes) / bytesPerSlab) + 1) * (ULONGLONG)blocksPerSlab + alignmentInBlocks;
4258         startingOffsetDelta = (startingLBA * fdoExtension->DiskGeometry.BytesPerSector) - StartingOffset;
4259     }
4260     else
4261     {
4262         startingLBA = ((StartingOffset - alignmentInBytes) / bytesPerSlab) * (ULONGLONG)blocksPerSlab + alignmentInBlocks;
4263         startingOffsetDelta = 0;
4264     }
4265 
4266     //
4267     // Caclulate the number of slabs the caller requested.
4268     //
4269     if ((LengthInBytes % bytesPerSlab) == 0) {
4270         requestedSlabs = (LengthInBytes / bytesPerSlab);
4271     } else {
4272         //
4273         // Round up the number of requested slabs if the length indicates a
4274         // partial slab.  This should cover the case where the user specifies
4275         // a dataset range for the whole disk, but the size of the disk is not
4276         // a slab-multiple.  Rounding up allows us to return the status of the
4277         // partial slab
4278         //
4279         requestedSlabs = (LengthInBytes / bytesPerSlab) + 1;
4280     }
4281 
4282     //
4283     // If the caller asked for no slabs then return STATUS_INVALID_PARAMETER.
4284     //
4285     if (requestedSlabs == 0)
4286     {
4287         TracePrint((TRACE_LEVEL_ERROR,
4288                     TRACE_FLAG_IOCTL,
4289                     "ClasspDeviceGetLBAStatusWorker (%p): Invalid number (%I64u) of slabs requested\n",
4290                     DeviceObject,
4291                     requestedSlabs));
4292 
4293         status = STATUS_INVALID_PARAMETER;
4294         goto Exit;
4295     }
4296 
4297     //
4298     // Cap requested slabs at MAXULONG since SlabAllocationBitMapBitCount
4299     // is a 4-byte field.  We may return less data than requested, but the
4300     // caller can simply re-query for the omitted portion(s).
4301     //
4302     requestedSlabs = min(requestedSlabs, MAXULONG);
4303 
4304     //
4305     // Calculate the required size of the output buffer based upon the desired
4306     // version of the output structure.
4307     // In the worst case, Get LBA Status returns a descriptor for each slab
4308     // requested, thus the required output buffer length is equal to:
4309     //  1. The size of DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT; plus
4310     //  2. The size of DEVICE_DATA_SET_LB_PROVISIONING_STATE(_V2); plus
4311     //  3. The size of a ULONG array large enough to hold a bit for each slab requested.
4312     //     (The first element is already allocated in DEVICE_DATA_SET_LB_PROVISIONING_STATE(_V2).)
4313     //
4314 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
4315     if (OutputVersion == DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2) {
4316 
4317         requiredOutputLength = (ULONG)(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT)
4318                                         + sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)
4319                                         + (((requestedSlabs - 1) / bitMapGranularityInBits))
4320                                            * FIELD_SIZE(DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2, SlabAllocationBitMap[0]));
4321 
4322     } else
4323 #else
4324     UNREFERENCED_PARAMETER(OutputVersion);
4325 #endif
4326     {
4327 
4328         requiredOutputLength = (ULONG)(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT)
4329                                         + sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE)
4330                                         + (((requestedSlabs - 1) / bitMapGranularityInBits))
4331                                            * FIELD_SIZE(DEVICE_DATA_SET_LB_PROVISIONING_STATE, SlabAllocationBitMap[0]));
4332     }
4333 
4334     //
4335     // The output buffer is not big enough to hold the requested data.
4336     // Inform the caller of the correct buffer size.
4337     //
4338     if (outputLength < requiredOutputLength)
4339     {
4340         status = STATUS_BUFFER_TOO_SMALL;
4341 
4342         TracePrint((TRACE_LEVEL_ERROR,
4343                     TRACE_FLAG_IOCTL,
4344                     "ClasspDeviceGetLBAStatusWorker (%p): Given output buffer is %u bytes, needs to be %u bytes\n",
4345                     DeviceObject,
4346                     outputLength,
4347                     requiredOutputLength));
4348 
4349         //
4350         // If the output buffer is big enough, write the required buffer
4351         // length to the first ULONG bytes of the output buffer.
4352         //
4353         if (outputLength >= sizeof(ULONG))
4354         {
4355             *((PULONG)DsmOutput) = requiredOutputLength;
4356         }
4357 
4358         goto Exit;
4359     }
4360 
4361     //
4362     // Calculate the maximum number of slabs that could be returned by a single
4363     // Get LBA Status command.  The max buffer size could either be capped by
4364     // the Parameter Data Length field or the Max Transfer Length of the
4365     // adapter.
4366     // The number of slabs we actually ask for in a single command is the
4367     // smaller of the number of slabs requested by the user or the max number
4368     // of slabs we can theoretically ask for in a single command.
4369     //
4370     maxBufferSize = MIN(MAXULONG, fdoExtension->PrivateFdoData->HwMaxXferLen);
4371     maxSlabs = (maxBufferSize - sizeof(LBA_STATUS_LIST_HEADER)) / sizeof(LBA_STATUS_DESCRIPTOR);
4372     slabsPerCommand = min(requestedSlabs, maxSlabs);
4373 
4374     //
4375     // Allocate the buffer that will contain the returned LBA Status Descriptors.
4376     // Assume that in the worst case every other slab has a different mapping
4377     // status.  That means that there may be a descriptor for every slab requested.
4378     //
4379     lbaStatusSize = (ULONG)(sizeof(LBA_STATUS_LIST_HEADER) + (slabsPerCommand * sizeof(LBA_STATUS_DESCRIPTOR)));
4380 #if defined(_ARM_) || defined(_ARM64_)
4381     //
4382     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
4383     // based platforms. We are taking the conservative approach here.
4384     //
4385     lbaStatusSize = ALIGN_UP_BY(lbaStatusSize,KeGetRecommendedSharedDataAlignment());
4386     lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, lbaStatusSize, CLASS_TAG_LB_PROVISIONING);
4387 #else
4388     lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNx, lbaStatusSize, CLASS_TAG_LB_PROVISIONING);
4389 #endif
4390 
4391     if (lbaStatusListHeader == NULL)
4392     {
4393         TracePrint((TRACE_LEVEL_ERROR,
4394                     TRACE_FLAG_IOCTL,
4395                     "ClasspDeviceGetLBAStatusWorker (%p): Failed to allocate %u bytes for descriptors\n",
4396                     DeviceObject,
4397                     lbaStatusSize));
4398 
4399         NT_ASSERT(lbaStatusListHeader != NULL);
4400         status = STATUS_INSUFFICIENT_RESOURCES;
4401         goto Exit;
4402     }
4403 
4404     //
4405     // Set default values for the output buffer.
4406     // If we process at least one slab from the device we will update the
4407     // offset and lengths accordingly.
4408     //
4409     DsmOutput->Action = DeviceDsmAction_Allocation;
4410     DsmOutput->Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT);
4411     DsmOutput->OutputBlockOffset = 0;
4412     DsmOutput->OutputBlockLength = 0;
4413     *DsmOutputLength = DsmOutput->Size;
4414 
4415     //
4416     // The returned DEVICE_DATA_SET_LB_PROVISIONING_STATE is at the end of the
4417     // DSM output structure.  Zero it out before we start to fill it in.
4418     //
4419     lbpState = Add2Ptr(DsmOutput, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT));
4420     RtlZeroMemory(lbpState, requiredOutputLength - sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT));
4421 
4422     do {
4423         //
4424         // Send down GetLBAStatus for the current range.
4425         //
4426         status = GetLBAStatus(fdoExtension,
4427                               Srb,
4428                               startingLBA,
4429                               lbaStatusListHeader,
4430                               lbaStatusSize,
4431                               ConsolidateableBlocksOnly);
4432 
4433         if (NT_SUCCESS(status))
4434         {
4435             ULONG descrIndex = 0;
4436             ULONG descrSize = 0;
4437             ULONG descrSizeOverhead;
4438             ULONG descrCount = 0;
4439             ULONGLONG expectedStartingLBA;
4440             BOOLEAN processCurrentDescriptor = TRUE;
4441             ULONG commandProcessedSlabs = 0; // Number of slabs processed for this command.
4442 
4443             descrSizeOverhead = FIELD_OFFSET(LBA_STATUS_LIST_HEADER, Descriptors[0]) -
4444                                 RTL_SIZEOF_THROUGH_FIELD(LBA_STATUS_LIST_HEADER, ParameterLength);
4445             REVERSE_BYTES(&descrSize, &(lbaStatusListHeader->ParameterLength));
4446 
4447             //
4448             // If the returned Parameter Data Length field describes more
4449             // descriptors than we allocated space for then make sure we don't
4450             // try to process more descriptors than are actually present.
4451             //
4452             if (descrSize > (lbaStatusSize - RTL_SIZEOF_THROUGH_FIELD(LBA_STATUS_LIST_HEADER, ParameterLength))) {
4453                 descrSize = (lbaStatusSize - RTL_SIZEOF_THROUGH_FIELD(LBA_STATUS_LIST_HEADER, ParameterLength));
4454             }
4455 
4456             if (descrSize >= descrSizeOverhead) {
4457                 descrSize -= descrSizeOverhead;
4458                 descrCount = descrSize / sizeof(LBA_STATUS_DESCRIPTOR);
4459 
4460                 //
4461                 // Make sure at least one descriptor was returned.
4462                 //
4463                 if (descrCount > 0) {
4464                     //
4465                     // We expect the first starting LBA returned by the device to be the
4466                     // same starting LBA we specified in the command.
4467                     //
4468                     expectedStartingLBA = startingLBA;
4469 
4470                     //
4471                     // Translate the returned LBA status descriptors into a bitmap where each bit represents
4472                     // a slab.  The slab size is represented by the Optimal Unmap Granularity.
4473                     // 1 = The slab is mapped.
4474                     // 0 = The slab is unmapped (deallocated or anchored).
4475                     //
4476                     for (descrIndex = 0; descrIndex < descrCount && totalProcessedSlabs < requestedSlabs && !doneProcessing; descrIndex++)
4477                     {
4478                         PLBA_STATUS_DESCRIPTOR lbaStatusDescr = &(lbaStatusListHeader->Descriptors[descrIndex]);
4479                         ULONGLONG returnedStartingLBA;
4480                         ULONG mapped = (lbaStatusDescr->ProvisioningStatus != LBA_STATUS_MAPPED) ? 0x0 : 0x1;
4481                         ULONG lbaCount = 0;
4482 
4483                         REVERSE_BYTES_QUAD(&returnedStartingLBA, &(lbaStatusDescr->StartingLBA));
4484                         REVERSE_BYTES(&lbaCount, &(lbaStatusDescr->LogicalBlockCount));
4485 
4486                         if (returnedStartingLBA != expectedStartingLBA)
4487                         {
4488                             //
4489                             // We expect the descriptors will express a contiguous range of LBAs.
4490                             // If the starting LBA is not contiguous with the LBA range from the
4491                             // previous descriptor then we should not process any more descriptors,
4492                             // including the current one.
4493                             //
4494                             TracePrint((TRACE_LEVEL_ERROR,
4495                                         TRACE_FLAG_IOCTL,
4496                                         "ClasspDeviceGetLBAStatusWorker (%p): Device returned starting LBA = %I64x when %I64x was expected.\n",
4497                                         DeviceObject,
4498                                         returnedStartingLBA,
4499                                         startingLBA));
4500 
4501                             doneProcessing = TRUE;
4502                             processCurrentDescriptor = FALSE;
4503                             *BlockLimitsDataMayHaveChanged = TRUE;
4504                         }
4505                         else if (lbaCount > 0 && lbaCount % blocksPerSlab != 0)
4506                         {
4507                             //
4508                             // If the device returned an LBA count with a partial slab, round
4509                             // the LBA count up to the nearest slab and set a flag to stop
4510                             // processing further descriptors.  This is mainly to handle the
4511                             // case where disk size may not be slab-aligned and thus the last
4512                             // "slab" is actually a partial slab.
4513                             //
4514                             TracePrint((TRACE_LEVEL_WARNING,
4515                                     TRACE_FLAG_IOCTL,
4516                                     "ClasspDeviceGetLBAStatusWorker (%p): Device returned an LBA count (%u) that is not a multiple of the slab size (%u)\n",
4517                                     DeviceObject,
4518                                     lbaCount,
4519                                     blocksPerSlab));
4520 
4521                             lbaCount = ((lbaCount / blocksPerSlab) + 1) * blocksPerSlab;
4522 
4523                             doneProcessing = TRUE;
4524                             processCurrentDescriptor = TRUE;
4525                         }
4526                         else if (lbaCount == 0)
4527                         {
4528                             //
4529                             // If the LBA count is 0, just skip this descriptor.
4530                             //
4531                             TracePrint((TRACE_LEVEL_WARNING,
4532                                     TRACE_FLAG_IOCTL,
4533                                     "ClasspDeviceGetLBAStatusWorker (%p): Device returned a zero LBA count\n",
4534                                     DeviceObject));
4535 
4536                             processCurrentDescriptor = FALSE;
4537                         }
4538 
4539                         //
4540                         // Generate bits for the slabs described in the current descriptor.
4541                         // It's possible the device may have returned more slabs than requested
4542                         // so we make sure to stop once we've processed all we need.
4543                         //
4544                         if (processCurrentDescriptor)
4545                         {
4546                             ULONG descrSlabs = lbaCount / blocksPerSlab; // Number of slabs in this descriptor.
4547 
4548                             for(; 0 < descrSlabs && totalProcessedSlabs < requestedSlabs; descrSlabs--, commandProcessedSlabs++, totalProcessedSlabs++)
4549                             {
4550                                 ULONG bitMapIndex = totalProcessedSlabs / bitMapGranularityInBits;
4551                                 ULONG bitPos = totalProcessedSlabs % bitMapGranularityInBits;
4552 
4553                                 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
4554                                 if (OutputVersion == DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2) {
4555                                     ((PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)lbpState)->SlabAllocationBitMap[bitMapIndex] |= (mapped << bitPos);
4556                                 } else
4557                                 #endif
4558                                 {
4559                                     lbpState->SlabAllocationBitMap[bitMapIndex] |= (mapped << bitPos);
4560                                 }
4561                             }
4562                         }
4563 
4564                         //
4565                         // Calculate the next expected starting LBA.
4566                         //
4567                         expectedStartingLBA = returnedStartingLBA + lbaCount;
4568                     }
4569 
4570                     if (commandProcessedSlabs > 0) {
4571 
4572                         //
4573                         // Calculate the starting LBA we'll use for the next command.
4574                         //
4575                         startingLBA += ((ULONGLONG)commandProcessedSlabs * (ULONGLONG)blocksPerSlab);
4576 
4577                     } else {
4578                         //
4579                         // This should never happen, but we should handle it gracefully anyway.
4580                         //
4581                         TracePrint((TRACE_LEVEL_ERROR,
4582                                     TRACE_FLAG_IOCTL,
4583                                     "ClasspDeviceGetLBAStatusWorker (%p): The slab allocation bitmap has zero length.\n",
4584                                     DeviceObject));
4585 
4586                         NT_ASSERT(commandProcessedSlabs != 0);
4587                         doneProcessing = TRUE;
4588                         status = STATUS_UNSUCCESSFUL;
4589                     }
4590                 } else {
4591                     TracePrint((TRACE_LEVEL_ERROR,
4592                                 TRACE_FLAG_IOCTL,
4593                                 "ClasspDeviceGetLBAStatusWorker (%p): Device returned no LBA Status Descriptors.\n",
4594                                 DeviceObject));
4595 
4596                     doneProcessing = TRUE;
4597                     status = STATUS_UNSUCCESSFUL;
4598                 }
4599             } else {
4600                 TracePrint((TRACE_LEVEL_ERROR,
4601                             TRACE_FLAG_IOCTL,
4602                             "ClasspDeviceGetLBAStatusWorker (%p): not enough bytes returned\n",
4603                             DeviceObject));
4604 
4605                 doneProcessing = TRUE;
4606                 status = STATUS_DEVICE_DATA_ERROR;
4607             }
4608         }
4609 
4610     //
4611     // Loop until we encounter some error or we've processed all the requested slabs.
4612     //
4613     } while (NT_SUCCESS(status) &&
4614              !doneProcessing &&
4615              (totalProcessedSlabs < requestedSlabs));
4616 
4617     //
4618     // At least one slab was returned by the device and processed, which we
4619     // consider success.  It's up to the caller to detect truncation.
4620     // Update the output buffer sizes, offsets, etc. accordingly.
4621     //
4622     if (totalProcessedSlabs > 0) {
4623 
4624         #if (NTDDI_VERSION >= NTDDI_WINBLUE)
4625         if (OutputVersion == DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2) {
4626             PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 lbpStateV2 = (PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)lbpState;
4627 
4628             lbpStateV2->SlabSizeInBytes = bytesPerSlab;
4629             lbpStateV2->SlabOffsetDeltaInBytes = startingOffsetDelta;
4630             lbpStateV2->SlabAllocationBitMapBitCount = totalProcessedSlabs;
4631             lbpStateV2->SlabAllocationBitMapLength = ((totalProcessedSlabs - 1) / (ULONGLONG)bitMapGranularityInBits) + 1;
4632             lbpStateV2->Version = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2;
4633 
4634             //
4635             // Note that there is already one element of the bitmap array allocated
4636             // in the DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 structure itself, which
4637             // is why we subtract 1 from SlabAllocationBitMapLength.
4638             //
4639             lbpStateV2->Size = sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)
4640                                + ((lbpStateV2->SlabAllocationBitMapLength - 1) * sizeof(lbpStateV2->SlabAllocationBitMap[0]));
4641 
4642         } else
4643         #endif
4644         {
4645 
4646             lbpState->SlabSizeInBytes = bytesPerSlab;
4647             lbpState->SlabOffsetDeltaInBytes = (ULONG)startingOffsetDelta;
4648             lbpState->SlabAllocationBitMapBitCount = totalProcessedSlabs;
4649             lbpState->SlabAllocationBitMapLength = ((totalProcessedSlabs - 1) / bitMapGranularityInBits) + 1;
4650             lbpState->Version = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1;
4651 
4652             //
4653             // Note that there is already one element of the bitmap array allocated
4654             // in the DEVICE_DATA_SET_LB_PROVISIONING_STATE structure itself, which
4655             // is why we subtract 1 from SlabAllocationBitMapLength.
4656             //
4657             lbpState->Size = sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE)
4658                              + ((lbpState->SlabAllocationBitMapLength - 1) * sizeof(lbpState->SlabAllocationBitMap[0]));
4659         }
4660 
4661         DsmOutput->OutputBlockLength = lbpState->Size; // Size is at the same offset in all versions of the structure.
4662         DsmOutput->OutputBlockOffset = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT);
4663         *DsmOutputLength = DsmOutput->Size + DsmOutput->OutputBlockLength;
4664 
4665         status = STATUS_SUCCESS;
4666     }
4667 
4668     TracePrint((TRACE_LEVEL_INFORMATION,
4669             TRACE_FLAG_IOCTL,
4670             "ClasspDeviceGetLBAStatusWorker (%p): Processed a total of %u slabs\n",
4671             DeviceObject,
4672             totalProcessedSlabs));
4673 Exit:
4674 
4675     FREE_POOL(lbaStatusListHeader);
4676     return status;
4677 }
4678 
ClassGetLBProvisioningLogPage(_In_ PDEVICE_OBJECT DeviceObject,_In_ PSCSI_REQUEST_BLOCK Srb,_In_ ULONG LogPageSize,_Inout_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage)4679 NTSTATUS ClassGetLBProvisioningLogPage(
4680     _In_ PDEVICE_OBJECT DeviceObject,
4681     _In_ PSCSI_REQUEST_BLOCK Srb,
4682     _In_ ULONG LogPageSize,
4683     _Inout_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage
4684     )
4685 /*
4686 Routine Description:
4687 
4688     This function sends a LOG SENSE command to the given device and returns the
4689     Logical Block Provisioning Log Page, if available.
4690 
4691 Arguments:
4692     DeviceObject: The FDO of the device to which the Log Sense command will be sent.
4693     Srb: This should be allocated before it is passed in, but it does not have
4694         to be initialized.  This function will initialize it.
4695     LogPageSize: The size of the LogPage buffer in bytes.
4696     LogPage: A pointer to an already allocated output buffer that may contain
4697         the LBP log page when this function returns.
4698 
4699 Return Value:
4700 
4701     STATUS_INVALID_PARAMETER: May be returned if the LogPage buffer is NULL or
4702         not large enough.
4703     STATUS_SUCCESS: The log page was obtained and placed in the LogPage buffer.
4704 
4705     This function may return other NTSTATUS codes from internal function calls.
4706 --*/
4707 {
4708     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
4709     NTSTATUS status = STATUS_SUCCESS;
4710     PCDB cdb = NULL;
4711 
4712     //
4713     // Make sure the caller passed in an adequate output buffer.  The Allocation
4714     // Length field in the Log Sense command is only 2 bytes so we need to also
4715     // make sure that the given log page size isn't larger than MAXUSHORT.
4716     //
4717     if (LogPage == NULL ||
4718         LogPageSize < sizeof(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING) ||
4719         LogPageSize > MAXUSHORT)
4720     {
4721         TracePrint((TRACE_LEVEL_ERROR,
4722                     TRACE_FLAG_GENERAL,
4723                     "ClassGetLBProvisioningLogPage: DO (%p), Invalid parameter, LogPage = %p, LogPageSize = %u.\n",
4724                     DeviceObject,
4725                     LogPage,
4726                     LogPageSize));
4727 
4728         return STATUS_INVALID_PARAMETER;
4729     }
4730 
4731     //
4732     // Initialize the SRB.
4733     //
4734     if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
4735         status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
4736                                                 STORAGE_ADDRESS_TYPE_BTL8,
4737                                                 CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
4738                                                 1,
4739                                                 SrbExDataTypeScsiCdb16);
4740         if (NT_SUCCESS(status)) {
4741             ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
4742         } else {
4743             //
4744             // Should not occur.
4745             //
4746             NT_ASSERT(FALSE);
4747         }
4748     } else {
4749         RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
4750         Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
4751         Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
4752     }
4753 
4754     //
4755     // Build and send down the Log Sense command.
4756     //
4757     SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
4758     SrbSetRequestTag(Srb, SP_UNTAGGED);
4759     SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
4760     SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
4761     SrbSetCdbLength(Srb, sizeof(cdb->LOGSENSE));
4762 
4763     cdb = SrbGetCdb(Srb);
4764     cdb->LOGSENSE.OperationCode = SCSIOP_LOG_SENSE;
4765     cdb->LOGSENSE.PageCode = LOG_PAGE_CODE_LOGICAL_BLOCK_PROVISIONING;
4766     cdb->LOGSENSE.PCBit = 0;
4767     cdb->LOGSENSE.ParameterPointer[0] = 0;
4768     cdb->LOGSENSE.ParameterPointer[1] = 0;
4769     REVERSE_BYTES_SHORT(&(cdb->LOGSENSE.AllocationLength), &LogPageSize);
4770 
4771     status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
4772                                         Srb,
4773                                         LogPage,
4774                                         LogPageSize,
4775                                         FALSE);
4776 
4777     //
4778     // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
4779     // buffer was larger than necessary.
4780     //
4781     if (status == STATUS_DATA_OVERRUN &&
4782         SrbGetDataTransferLength(Srb) < LogPageSize)
4783     {
4784         status = STATUS_SUCCESS;
4785     }
4786 
4787     //
4788     // Log the command.
4789     //
4790     TracePrint((TRACE_LEVEL_INFORMATION,
4791                 TRACE_FLAG_IOCTL,
4792                 "ClassGetLBProvisioningLogPage: DO (%p), LogSense command issued for LBP log page. NT Status: %!STATUS!.\n",
4793                 DeviceObject,
4794                 status
4795                 ));
4796 
4797     return status;
4798 }
4799 
ClassInterpretLBProvisioningLogPage(_In_ PDEVICE_OBJECT DeviceObject,_In_ ULONG LogPageSize,_In_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage,_In_ ULONG ResourcesSize,_Out_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources)4800 NTSTATUS ClassInterpretLBProvisioningLogPage(
4801     _In_ PDEVICE_OBJECT DeviceObject,
4802     _In_ ULONG LogPageSize,
4803     _In_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage,
4804     _In_ ULONG ResourcesSize,
4805     _Out_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources
4806     )
4807 /*
4808 Routine Description:
4809 
4810     This function takes a Logical Block Provisioning log page (returned by
4811     ClassGetLBProvisioningLogPage(), for example), interprets its contents,
4812     and returns the interpreted data in a STORAGE_LB_PROVISIONING_MAP_RESOURCES
4813     structure.
4814 
4815     None, some, or all of the data in the output buffer may be valid.  The
4816     caller must look at the individual "Valid" fields to see which fields have
4817     valid data.
4818 
4819 Arguments:
4820     DeviceObject: The FDO of the device from which the log page was obtained.
4821     LogPageSize: The size of the LogPage buffer in bytes.
4822     LogPage: A pointer to a valid LBP log page structure.
4823     ResourcesSize: The size of the Resources buffer in bytes.
4824     Resources: A pointer to an already allocated output buffer that may contain
4825         the interpreted log page data when this function returns.
4826 
4827 Return Value:
4828 
4829     STATUS_NOT_SUPPORTED: May be returned if the threshold exponent from the
4830         0xB2 page is invalid.
4831     STATUS_INVALID_PARAMETER: May be returned if either the LogPage or Resources
4832         buffers are NULL or too small.
4833     STATUS_SUCCESS: The log page data was interpreted and the Resources output
4834         buffer has data in it.
4835 
4836     This function may return other NTSTATUS codes from internal function calls.
4837 --*/
4838 {
4839     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
4840     USHORT pageLength;
4841     PLOG_PARAMETER_HEADER parameter;
4842     PVOID endOfPage;
4843     USHORT parameterCode;
4844     ULONG resourceCount;
4845     UCHAR thresholdExponent = fdoExtension->FunctionSupportInfo->LBProvisioningData.ThresholdExponent;
4846     ULONGLONG thresholdSetSize;
4847 
4848     //
4849     // SBC-3 states that the threshold exponent (from the 0xB2 VPD page), must
4850     // be non-zero and less than or equal to 32.
4851     //
4852     if (thresholdExponent < 0 || thresholdExponent > 32)
4853     {
4854         TracePrint((TRACE_LEVEL_ERROR,
4855             TRACE_FLAG_GENERAL,
4856             "ClassInterpretLBProvisioningLogPage: DO (%p), Threshold Exponent (%u) is invalid.\n",
4857             DeviceObject,
4858             thresholdExponent));
4859 
4860         return STATUS_NOT_SUPPORTED;
4861     }
4862 
4863     if (Resources == NULL ||
4864         ResourcesSize < sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES) ||
4865         LogPage == NULL ||
4866         LogPageSize < sizeof(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING))
4867     {
4868         TracePrint((TRACE_LEVEL_ERROR,
4869             TRACE_FLAG_GENERAL,
4870             "ClassInterpretLBProvisioningLogPage: DO (%p), Invalid parameter, Resources = %p, ResourcesSize = %u, LogPage = %p, LogPageSize = %u.\n",
4871             DeviceObject,
4872             Resources,
4873             ResourcesSize,
4874             LogPage,
4875             LogPageSize));
4876 
4877         return STATUS_INVALID_PARAMETER;
4878     }
4879 
4880     //
4881     // Calculate the threshold set size (in LBAs).
4882     //
4883     thresholdSetSize = 1ULL << thresholdExponent;
4884 
4885     REVERSE_BYTES_SHORT(&pageLength, &(LogPage->PageLength));
4886 
4887     //
4888     // Initialize the output buffer.
4889     //
4890     RtlZeroMemory(Resources, sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES));
4891     Resources->Size = sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES);
4892     Resources->Version = sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES);
4893 
4894     //
4895     // Make sure we don't walk off the end of the log page buffer
4896     // if pageLength is somehow longer than the buffer itself.
4897     //
4898     pageLength = (USHORT)min(pageLength, (LogPageSize - FIELD_OFFSET(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING, Parameters)));
4899 
4900     parameter = (PLOG_PARAMETER_HEADER)((PUCHAR)LogPage + FIELD_OFFSET(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING, Parameters));
4901     endOfPage = (PVOID)((PUCHAR)parameter + pageLength);
4902 
4903     //
4904     // Walk the parameters.
4905     //
4906     while ((PVOID)parameter < endOfPage)
4907     {
4908         if (parameter->ParameterLength > 0)
4909         {
4910             REVERSE_BYTES_SHORT(&parameterCode, &(parameter->ParameterCode));
4911             switch(parameterCode)
4912             {
4913                 case LOG_PAGE_LBP_PARAMETER_CODE_AVAILABLE:
4914                 {
4915                     REVERSE_BYTES(&resourceCount, &(((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->ResourceCount));
4916                     Resources->AvailableMappingResources = (ULONGLONG)resourceCount * thresholdSetSize * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
4917                     Resources->AvailableMappingResourcesValid = TRUE;
4918 
4919                     //
4920                     // Devices that implement SBC-3 revisions older than r27 will not specify
4921                     // an LBP log page parameter that has fields beyond ResourceCount.
4922                     //
4923                     if (parameter->ParameterLength > FIELD_OFFSET(LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT, ResourceCount[3])) {
4924                         Resources->AvailableMappingResourcesScope = ((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->Scope;
4925                     }
4926 
4927                     break;
4928                 }
4929 
4930                 case LOG_PAGE_LBP_PARAMETER_CODE_USED:
4931                 {
4932                     REVERSE_BYTES(&resourceCount, &(((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->ResourceCount));
4933                     Resources->UsedMappingResources = (ULONGLONG)resourceCount * thresholdSetSize * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
4934                     Resources->UsedMappingResourcesValid = TRUE;
4935 
4936                     //
4937                     // Devices that implement SBC-3 revisions older than r27 will not specify
4938                     // an LBP log page parameter that has fields beyond ResourceCount.
4939                     //
4940                     if (parameter->ParameterLength > FIELD_OFFSET(LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT, ResourceCount[3])) {
4941                         Resources->UsedMappingResourcesScope = ((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->Scope;
4942                     }
4943 
4944                     break;
4945                 }
4946             }
4947         }
4948 
4949         //
4950         // Move to the next parameter.
4951         //
4952         parameter = (PLOG_PARAMETER_HEADER)((PUCHAR)parameter + sizeof(LOG_PARAMETER_HEADER) + parameter->ParameterLength);
4953     }
4954 
4955     return STATUS_SUCCESS;
4956 }
4957 
ClassGetLBProvisioningResources(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PSCSI_REQUEST_BLOCK Srb,_In_ ULONG ResourcesSize,_Inout_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources)4958 NTSTATUS ClassGetLBProvisioningResources(
4959     _In_ PDEVICE_OBJECT DeviceObject,
4960     _Inout_ PSCSI_REQUEST_BLOCK Srb,
4961     _In_ ULONG ResourcesSize,
4962     _Inout_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources
4963     )
4964 /*
4965 Routine Description:
4966 
4967     This function obtains the Logical Block Provisioning log page, interprets
4968     its contents, and returns the interpreted data in a
4969     STORAGE_LB_PROVISIONING_MAP_RESOURCES structure.
4970 
4971     None, some, or all of the data in the output buffer may be valid.  The
4972     caller must look at the individual "Valid" fields to see which fields have
4973     valid data.
4974 
4975 Arguments:
4976     DeviceObject: The target FDO.
4977     Srb: This should be allocated before it is passed in, but it does not have
4978         to be initialized.
4979     ResourcesSize: The size of the Resources buffer in bytes.
4980     Resources: A pointer to an already allocated output buffer that may contain
4981         the interpreted log page data when this function returns.
4982 
4983 Return Value:
4984 
4985     STATUS_NOT_SUPPORTED: May be returned if the device does not have LBP enabled.
4986     STATUS_INVALID_PARAMETER: May be returned if either the Resources buffer is
4987         NULL or too small.
4988     STATUS_INSUFFICIENT_RESOURCES: May be returned if a log page buffer could not
4989         be allocated.
4990     STATUS_SUCCESS: The log page data was obtained and the Resources output
4991         buffer has data in it.
4992 
4993     This function may return other NTSTATUS codes from internal function calls.
4994 --*/
4995 {
4996     NTSTATUS status;
4997     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
4998     ULONG logPageSize;
4999     PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING logPage = NULL;
5000 
5001     //
5002     // This functionality is only supported for devices that support logical
5003     // block provisioning.
5004     //
5005     if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == FALSE)
5006     {
5007         TracePrint((TRACE_LEVEL_ERROR,
5008             TRACE_FLAG_GENERAL,
5009             "ClassGetLBProvisioningResources: DO (%p), Device does not support logical block provisioning.\n",
5010             DeviceObject));
5011 
5012         return STATUS_NOT_SUPPORTED;
5013     }
5014 
5015     //
5016     // Validate the output buffer.
5017     //
5018     if (Resources == NULL ||
5019         ResourcesSize < sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES))
5020     {
5021         TracePrint((TRACE_LEVEL_ERROR,
5022             TRACE_FLAG_GENERAL,
5023             "ClassGetLBProvisioningResources: DO (%p), Invalid parameter, Resources = %p, ResourcesSize = %u.\n",
5024             DeviceObject,
5025             Resources,
5026             ResourcesSize));
5027 
5028         return STATUS_INVALID_PARAMETER;
5029     }
5030 
5031     //
5032     // Allocate a buffer for the log page.  Currently the log page contains:
5033     // 1. Log page header
5034     // 2. Log page parameter for used resources
5035     // 3. Log page parameter for available resources
5036     //
5037     logPageSize = sizeof(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING) + (2 * sizeof(LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT));
5038 
5039 #if defined(_ARM_) || defined(_ARM64_)
5040     //
5041     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
5042     // based platforms. We are taking the conservative approach here.
5043     //
5044     logPageSize = ALIGN_UP_BY(logPageSize, KeGetRecommendedSharedDataAlignment());
5045     logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, logPageSize,  CLASS_TAG_LB_PROVISIONING);
5046 #else
5047     logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING)ExAllocatePoolWithTag(NonPagedPoolNx, logPageSize,  CLASS_TAG_LB_PROVISIONING);
5048 #endif
5049     if (logPage != NULL)
5050     {
5051         //
5052         // Get the LBP log page from the device.
5053         //
5054         status = ClassGetLBProvisioningLogPage(DeviceObject,
5055                                                Srb,
5056                                                logPageSize,
5057                                                logPage);
5058 
5059         if (NT_SUCCESS(status))
5060         {
5061             //
5062             // Interpret the log page and fill in the output buffer.
5063             //
5064             status = ClassInterpretLBProvisioningLogPage(DeviceObject,
5065                                                          logPageSize,
5066                                                          logPage,
5067                                                          ResourcesSize,
5068                                                          Resources);
5069         }
5070 
5071         ExFreePool(logPage);
5072     }
5073     else
5074     {
5075         TracePrint((TRACE_LEVEL_ERROR,
5076                     TRACE_FLAG_GENERAL,
5077                     "ClassGetLBProvisioningResources: DO (%p), Failed to allocate memory for LBP log page.\n",
5078                     DeviceObject));
5079 
5080         status = STATUS_INSUFFICIENT_RESOURCES;
5081     }
5082 
5083     return status;
5084 }
5085 
5086 NTSTATUS
ClassDeviceGetLBProvisioningResources(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)5087 ClassDeviceGetLBProvisioningResources(
5088     _In_ PDEVICE_OBJECT DeviceObject,
5089     _Inout_ PIRP Irp,
5090     _Inout_ PSCSI_REQUEST_BLOCK Srb
5091     )
5092 /*
5093 Routine Description:
5094 
5095     This function returns the LBP resource counts in a
5096     STORAGE_LB_PROVISIONING_MAP_RESOURCES structure in the IRP.
5097 
5098     None, some, or all of the data in the output buffer may be valid.  The
5099     caller must look at the individual "Valid" fields to see which fields have
5100     valid data.
5101 
5102 Arguments:
5103     DeviceObject: The target FDO.
5104     Irp: The IRP which will contain the output buffer upon completion.
5105     Srb: This should be allocated before it is passed in, but it does not have
5106         to be initialized.
5107 
5108 Return Value:
5109 
5110     Some NTSTATUS code.
5111 
5112 --*/
5113 {
5114     NTSTATUS status;
5115     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
5116     PSTORAGE_LB_PROVISIONING_MAP_RESOURCES mapResources = (PSTORAGE_LB_PROVISIONING_MAP_RESOURCES)Irp->AssociatedIrp.SystemBuffer;
5117 
5118     status = ClassGetLBProvisioningResources(DeviceObject,
5119                                            Srb,
5120                                            irpStack->Parameters.DeviceIoControl.OutputBufferLength,
5121                                            mapResources);
5122 
5123     if (NT_SUCCESS(status)) {
5124         Irp->IoStatus.Information = mapResources->Size;
5125     } else {
5126         Irp->IoStatus.Information = 0;
5127     }
5128 
5129     Irp->IoStatus.Status = status;
5130     ClassReleaseRemoveLock(DeviceObject, Irp);
5131     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5132 
5133     return status;
5134 }
5135 
5136 _Function_class_(IO_WORKITEM_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)5137 _IRQL_requires_(PASSIVE_LEVEL)
5138 _IRQL_requires_same_
5139 VOID
5140 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
5141 ClassLogThresholdEvent(
5142     _In_ PDEVICE_OBJECT DeviceObject,
5143     _In_opt_ PVOID Context
5144     )
5145 /*
5146     Routine Description:
5147 
5148     This function logs a logical block provisioning soft threshold event to the
5149     system event log.
5150 
5151 Arguments:
5152     DeviceObject: The FDO that represents the device that reported the soft
5153         threshold.
5154     Context: A pointer to the IO_WORKITEM in which this function is running.
5155 
5156 --*/
5157 {
5158     NTSTATUS status = STATUS_SUCCESS;
5159     PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
5160     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
5161     PSCSI_REQUEST_BLOCK srb = NULL;
5162     STORAGE_LB_PROVISIONING_MAP_RESOURCES resources = {0};
5163     ULONG resourcesSize = sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES);
5164     PIO_ERROR_LOG_PACKET errorLogEntry = NULL;
5165     ULONG logEntrySize = sizeof(IO_ERROR_LOG_PACKET);
5166     PWCHAR stringIndex = NULL;
5167     LONG stringSize = 0;
5168     ULONG srbSize;
5169 
5170     //
5171     // Allocate an SRB for getting the LBP log page.
5172     //
5173     if ((fdoExtension->AdapterDescriptor != NULL) &&
5174         (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
5175         srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
5176     } else {
5177         srbSize = sizeof(SCSI_REQUEST_BLOCK);
5178     }
5179 
5180     srb = ExAllocatePoolWithTag(NonPagedPoolNx,
5181                                 srbSize,
5182                                 'ACcS');
5183     if (srb != NULL) {
5184 
5185         //
5186         // Try to get the LBP resources from the device so we can report them in
5187         // the system event log.
5188         //
5189         ClassGetLBProvisioningResources(DeviceObject,
5190                                         srb,
5191                                         resourcesSize,
5192                                         &resources);
5193 
5194         //
5195         // We need to allocate enough space for 3 insertion strings:
5196         // The first is a ULONG representing the disk number in decimal, which means
5197         // a max of 10 digits, plus one for the NULL character.
5198         // The second and third are ULONGLONGs representing the used and available
5199         // bytes, which means a max of 20 digits, plus one for the NULL character.
5200         // Make sure we do not exceed the max error log size or the max size of a
5201         // UCHAR since the size gets truncated to a UCHAR when we pass it to
5202         // IoAllocateErrorLogEntry().
5203         //
5204         logEntrySize = sizeof(IO_ERROR_LOG_PACKET) + (11 * sizeof(WCHAR)) + (2 * (21 * sizeof(WCHAR)));
5205         logEntrySize = min(logEntrySize, ERROR_LOG_MAXIMUM_SIZE);
5206         logEntrySize = min(logEntrySize, MAXUCHAR);
5207 
5208         errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceObject, (UCHAR)logEntrySize);
5209         if (errorLogEntry != NULL)
5210         {
5211             //
5212             // There are two event IDs we can use here.  Both use the disk number,
5213             // but one reports the available and used bytes while the other does not.
5214             // We fall back on the latter if we failed to obtain the available and
5215             // used byte counts from the LBP log page.
5216             //
5217             // The event insertion strings need to be in this order:
5218             // 1. The disk number. (Both event IDs use this.)
5219             // 2. Bytes used.
5220             // 3. Bytes available.
5221             //
5222 
5223             RtlZeroMemory(errorLogEntry, logEntrySize);
5224             errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET);
5225 
5226             stringIndex = (PWCHAR)((ULONG_PTR)errorLogEntry + sizeof(IO_ERROR_LOG_PACKET));
5227             stringSize = logEntrySize - sizeof(IO_ERROR_LOG_PACKET);
5228 
5229             //
5230             // Add the disk number to the insertion strings.
5231             //
5232             status = RtlStringCbPrintfW(stringIndex, stringSize, L"%d", fdoExtension->DeviceNumber);
5233 
5234             if (NT_SUCCESS(status) )
5235             {
5236                 errorLogEntry->NumberOfStrings++;
5237 
5238                 if (resources.UsedMappingResourcesValid &&
5239                     resources.AvailableMappingResourcesValid)
5240                 {
5241                     //
5242                     // Add the used mapping resources to the insertion strings.
5243                     //
5244                     stringIndex += (wcslen(stringIndex) + 1);
5245                     stringSize -= (LONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
5246 
5247                     status = RtlStringCbPrintfW(stringIndex, stringSize, L"%I64u", resources.UsedMappingResources);
5248 
5249                     if (NT_SUCCESS(status))
5250                     {
5251                         errorLogEntry->NumberOfStrings++;
5252 
5253                         //
5254                         // Add the available mapping resources to the insertion strings.
5255                         //
5256                         stringIndex += (wcslen(stringIndex) + 1);
5257                         stringSize -= (LONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
5258 
5259                         status = RtlStringCbPrintfW(stringIndex, stringSize, L"%I64u", resources.AvailableMappingResources);
5260 
5261                         if (NT_SUCCESS(status))
5262                         {
5263                             errorLogEntry->NumberOfStrings++;
5264                         }
5265                     }
5266                 }
5267                 else
5268                 {
5269                     TracePrint((TRACE_LEVEL_WARNING,
5270                                 TRACE_FLAG_GENERAL,
5271                                 "ClassLogThresholdEvent: DO (%p), Used and available mapping resources were unavailable.\n",
5272                                 DeviceObject));
5273                 }
5274             }
5275 
5276             //
5277             // If we were able to successfully assemble all 3 insertion strings,
5278             // then we can use one of the "extended" event IDs.  Otherwise, use the basic
5279             // event ID, which only requires the disk number.
5280             //
5281             if (errorLogEntry->NumberOfStrings == 3)
5282             {
5283                 if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN &&
5284                     resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN) {
5285 
5286                     errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_LUN_LUN;
5287 
5288                 } else if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN &&
5289                            resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN) {
5290 
5291                     errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_LUN_POOL;
5292 
5293                 } else if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN &&
5294                            resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN) {
5295 
5296                     errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_POOL_LUN;
5297 
5298                 } else if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN &&
5299                            resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN) {
5300 
5301                     errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_POOL_POOL;
5302 
5303                 } else {
5304 
5305                     errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX;
5306                 }
5307             }
5308             else
5309             {
5310                 errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED;
5311             }
5312 
5313             //
5314             // Write the error log packet to the system error logging thread.
5315             // It will be freed automatically.
5316             //
5317             IoWriteErrorLogEntry(errorLogEntry);
5318 
5319             TracePrint((TRACE_LEVEL_INFORMATION,
5320                         TRACE_FLAG_GENERAL,
5321                         "ClassLogThresholdEvent: DO (%p), Soft threshold notification logged.\n",
5322                         DeviceObject));
5323         }
5324         else
5325         {
5326             TracePrint((TRACE_LEVEL_ERROR,
5327                         TRACE_FLAG_GENERAL,
5328                         "ClassLogThresholdEvent: DO (%p), Failed to allocate memory for error log entry.\n",
5329                         DeviceObject));
5330         }
5331     } else {
5332         TracePrint((TRACE_LEVEL_ERROR,
5333                         TRACE_FLAG_GENERAL,
5334                         "ClassLogThresholdEvent: DO (%p), Failed to allocate memory for SRB.\n",
5335                         DeviceObject));
5336     }
5337 
5338 
5339     //
5340     // Clear the soft threshold event pending flag so that another can be queued.
5341     //
5342     InterlockedExchange((PLONG)&(fdoExtension->FunctionSupportInfo->LBProvisioningData.SoftThresholdEventPending), 0);
5343 
5344     ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
5345 
5346     FREE_POOL(srb);
5347 
5348     if (workItem != NULL) {
5349         IoFreeWorkItem(workItem);
5350     }
5351 }
5352 
5353 NTSTATUS
ClasspLogSystemEventWithDeviceNumber(_In_ PDEVICE_OBJECT DeviceObject,_In_ NTSTATUS IoErrorCode)5354 ClasspLogSystemEventWithDeviceNumber(
5355     _In_ PDEVICE_OBJECT DeviceObject,
5356     _In_ NTSTATUS IoErrorCode
5357     )
5358 /*
5359     Routine Description:
5360 
5361     This function is a helper routine to log any system events that require
5362     the DeviceNumber (e.g. disk number). It is basically a wrapper for the
5363     IoWriteErrorLogEntry call.
5364 
5365 Arguments:
5366     DeviceObject: The FDO that represents the device for which the event needs to be logged.
5367     IoErrorCode: The IO error code for the event.
5368 
5369 Return Value:
5370     STATUS_SUCCESS - if the event was logged
5371     STATUS_INSUFFICIENT_RESOURCES - otherwise
5372 
5373 --*/
5374 {
5375     NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
5376     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
5377     PIO_ERROR_LOG_PACKET errorLogEntry = NULL;
5378     ULONG logEntrySize = sizeof(IO_ERROR_LOG_PACKET);
5379     PWCHAR stringIndex = NULL;
5380     LONG stringSize = 0;
5381 
5382     //
5383     // We need to allocate enough space for one insertion string: a ULONG
5384     // representing the disk number in decimal, which means a max of 10 digits,
5385     // plus one for the NULL character.
5386     // Make sure we do not exceed the max error log size or the max size of a
5387     // UCHAR since the size gets truncated to a UCHAR when we pass it to
5388     // IoAllocateErrorLogEntry().
5389     //
5390     logEntrySize = sizeof(IO_ERROR_LOG_PACKET) + (11 * sizeof(WCHAR));
5391     logEntrySize = min(logEntrySize, ERROR_LOG_MAXIMUM_SIZE);
5392     logEntrySize = min(logEntrySize, MAXUCHAR);
5393 
5394     errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceObject, (UCHAR)logEntrySize);
5395     if (errorLogEntry) {
5396 
5397         RtlZeroMemory(errorLogEntry, logEntrySize);
5398         errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET);
5399         errorLogEntry->ErrorCode = IoErrorCode;
5400 
5401         stringIndex = (PWCHAR)((ULONG_PTR)errorLogEntry + sizeof(IO_ERROR_LOG_PACKET));
5402         stringSize = logEntrySize - sizeof(IO_ERROR_LOG_PACKET);
5403 
5404         //
5405         // Add the disk number to the insertion strings.
5406         //
5407         status = RtlStringCbPrintfW(stringIndex, stringSize, L"%d", fdoExtension->DeviceNumber);
5408 
5409         if (NT_SUCCESS(status)) {
5410             errorLogEntry->NumberOfStrings++;
5411         }
5412 
5413         //
5414         // Write the error log packet to the system error logging thread.
5415         // It will be freed automatically.
5416         //
5417         IoWriteErrorLogEntry(errorLogEntry);
5418 
5419         status = STATUS_SUCCESS;
5420     }
5421 
5422     return status;
5423 }
5424 
5425 _Function_class_(IO_WORKITEM_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)5426 _IRQL_requires_(PASSIVE_LEVEL)
5427 _IRQL_requires_same_
5428 VOID
5429 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
5430 ClassLogResourceExhaustionEvent(
5431     _In_ PDEVICE_OBJECT DeviceObject,
5432     _In_opt_ PVOID Context
5433     )
5434 /*
5435     Routine Description:
5436 
5437     This function logs a logical block provisioning permanent resource exhaustion
5438     event to the system event log.
5439 
5440 Arguments:
5441     DeviceObject: The FDO that represents the device that reported the permanent
5442         resource exhaustion.
5443     Context: A pointer to the IO_WORKITEM in which this function is running.
5444 
5445 --*/
5446 {
5447     PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
5448 
5449     if (NT_SUCCESS(ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_ERROR_DISK_RESOURCES_EXHAUSTED))) {
5450 
5451         TracePrint((TRACE_LEVEL_INFORMATION,
5452                     TRACE_FLAG_GENERAL,
5453                     "ClassLogResourceExhaustionEvent: DO (%p), Permanent resource exhaustion logged.\n",
5454                     DeviceObject));
5455     } else {
5456         TracePrint((TRACE_LEVEL_ERROR,
5457                     TRACE_FLAG_GENERAL,
5458                     "ClassLogResourceExhaustionEvent: DO (%p), Failed to allocate memory for error log entry.\n",
5459                     DeviceObject));
5460     }
5461 
5462 
5463     ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
5464 
5465     if (workItem != NULL) {
5466         IoFreeWorkItem(workItem);
5467     }
5468 }
5469 
5470 
ClassQueueThresholdEventWorker(_In_ PDEVICE_OBJECT DeviceObject)5471 VOID ClassQueueThresholdEventWorker(
5472     _In_ PDEVICE_OBJECT DeviceObject
5473     )
5474 /*
5475 Routine Description:
5476 
5477     This function queues a delayed work item that will eventually log a
5478     logical block provisioning soft threshold event to the system event log.
5479 
5480 Arguments:
5481     DeviceObject: The FDO that represents the device that reported the soft
5482         threshold.
5483 
5484 --*/
5485 {
5486     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
5487     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
5488     PIO_WORKITEM workItem = NULL;
5489 
5490     if (commonExtension->IsFdo &&
5491         InterlockedCompareExchange((PLONG)&(fdoExtension->FunctionSupportInfo->LBProvisioningData.SoftThresholdEventPending), 1, 0) == 0)
5492     {
5493         workItem = IoAllocateWorkItem(DeviceObject);
5494 
5495         if (workItem)
5496         {
5497 
5498             TracePrint((TRACE_LEVEL_INFORMATION,
5499                         TRACE_FLAG_GENERAL,
5500                         "ClassQueueThresholdEventWorker: DO (%p), Queueing soft threshold notification work item.\n",
5501                         DeviceObject));
5502 
5503 
5504             ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
5505 
5506             //
5507             // Queue a work item to write the threshold notification to the
5508             // system event log.
5509             //
5510             IoQueueWorkItem(workItem, ClassLogThresholdEvent, DelayedWorkQueue, workItem);
5511         }
5512         else
5513         {
5514             //
5515             // Clear the soft threshold event pending flag since this is normally
5516             // done when the work item completes.
5517             //
5518             InterlockedExchange((PLONG)&(fdoExtension->FunctionSupportInfo->LBProvisioningData.SoftThresholdEventPending), 0);
5519 
5520             TracePrint((TRACE_LEVEL_ERROR,
5521                         TRACE_FLAG_GENERAL,
5522                         "ClassQueueThresholdEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
5523                         DeviceObject));
5524         }
5525     }
5526 }
5527 
ClassQueueResourceExhaustionEventWorker(_In_ PDEVICE_OBJECT DeviceObject)5528 VOID ClassQueueResourceExhaustionEventWorker(
5529     _In_ PDEVICE_OBJECT DeviceObject
5530     )
5531 /*
5532 Routine Description:
5533 
5534     This function queues a delayed work item that will eventually log a
5535     logical block provisioning permanent resource exhaustion event to the
5536     system event log.
5537 
5538 Arguments:
5539     DeviceObject: The FDO that represents the device that reported the resource
5540         exhaustion.
5541 
5542 --*/
5543 {
5544     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
5545     PIO_WORKITEM workItem = NULL;
5546 
5547     if (commonExtension->IsFdo)
5548     {
5549         workItem = IoAllocateWorkItem(DeviceObject);
5550 
5551         if (workItem)
5552         {
5553 
5554             TracePrint((TRACE_LEVEL_INFORMATION,
5555                         TRACE_FLAG_GENERAL,
5556                         "ClassQueueResourceExhaustionEventWorker: DO (%p), Queueing permanent resource exhaustion event work item.\n",
5557                         DeviceObject));
5558 
5559             ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
5560 
5561             //
5562             // Queue a work item to write the threshold notification to the
5563             // system event log.
5564             //
5565             IoQueueWorkItem(workItem, ClassLogResourceExhaustionEvent, DelayedWorkQueue, workItem);
5566         }
5567         else
5568         {
5569             TracePrint((TRACE_LEVEL_ERROR,
5570                         TRACE_FLAG_GENERAL,
5571                         "ClassQueueResourceExhaustionEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
5572                         DeviceObject));
5573         }
5574     }
5575 }
5576 
5577 _Function_class_(IO_WORKITEM_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)5578 _IRQL_requires_(PASSIVE_LEVEL)
5579 _IRQL_requires_same_
5580 VOID
5581 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
5582 ClassLogCapacityChangedProcess(
5583     _In_ PDEVICE_OBJECT DeviceObject,
5584     _In_opt_ PVOID Context
5585     )
5586 /*
5587     Routine Description:
5588 
5589     This function logs a capacity changed event to the system event log.
5590 
5591 Arguments:
5592     DeviceObject: The FDO that represents the device that reported the capacity change.
5593     Context: A pointer to the IO_WORKITEM in which this function is running.
5594 
5595 --*/
5596 {
5597     NTSTATUS     status;
5598     PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
5599 
5600     status = ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_CAPACITY_CHANGED);
5601 
5602     if (NT_SUCCESS(status)) {
5603 
5604         TracePrint((TRACE_LEVEL_INFORMATION,
5605                     TRACE_FLAG_GENERAL,
5606                     "ClassLogCapacityChangedEvent: DO (%p), Capacity changed logged.\n",
5607                     DeviceObject));
5608     } else {
5609         TracePrint((TRACE_LEVEL_ERROR,
5610                     TRACE_FLAG_GENERAL,
5611                     "ClassLogCapacityChangedEvent: DO (%p), Failed to allocate memory for error log entry.\n",
5612                     DeviceObject));
5613     }
5614 
5615     //
5616     // Get disk capacity and notify upper layer if capacity is changed.
5617     //
5618     status = ClassReadDriveCapacity(DeviceObject);
5619 
5620     if (!NT_SUCCESS(status)) {
5621         TracePrint((TRACE_LEVEL_ERROR,
5622                     TRACE_FLAG_GENERAL,
5623                     "ClassLogCapacityChangedEvent: DO (%p), ClassReadDriveCapacity returned %!STATUS!.\n",
5624                     DeviceObject,
5625                     status));
5626     }
5627 
5628     ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
5629 
5630     if (workItem != NULL) {
5631         IoFreeWorkItem(workItem);
5632     }
5633 }
5634 
5635 
5636 VOID
ClassQueueCapacityChangedEventWorker(_In_ PDEVICE_OBJECT DeviceObject)5637 ClassQueueCapacityChangedEventWorker(
5638     _In_ PDEVICE_OBJECT DeviceObject
5639     )
5640 /*
5641 Routine Description:
5642 
5643     This function queues a delayed work item that will eventually log a
5644     disk capacity changed event to the system event log.
5645 
5646 Arguments:
5647     DeviceObject: The FDO that represents the device that reported the capacity change.
5648 
5649 --*/
5650 {
5651     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
5652     PIO_WORKITEM workItem = NULL;
5653 
5654     if (commonExtension->IsFdo)
5655     {
5656         workItem = IoAllocateWorkItem(DeviceObject);
5657 
5658         if (workItem)
5659         {
5660 
5661             TracePrint((TRACE_LEVEL_INFORMATION,
5662                         TRACE_FLAG_GENERAL,
5663                         "ClassQueueCapacityChangedEventWorker: DO (%p), Queueing capacity changed event work item.\n",
5664                         DeviceObject));
5665 
5666             ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
5667 
5668             //
5669             // Queue a work item to write the threshold notification to the
5670             // system event log.
5671             //
5672             IoQueueWorkItem(workItem, ClassLogCapacityChangedProcess, DelayedWorkQueue, workItem);
5673         }
5674         else
5675         {
5676             TracePrint((TRACE_LEVEL_ERROR,
5677                         TRACE_FLAG_GENERAL,
5678                         "ClassQueueCapacityChangedEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
5679                         DeviceObject));
5680         }
5681     }
5682 }
5683 
5684 _Function_class_(IO_WORKITEM_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)5685 _IRQL_requires_(PASSIVE_LEVEL)
5686 _IRQL_requires_same_
5687 VOID
5688 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
5689 ClassLogProvisioningTypeChangedEvent(
5690     PDEVICE_OBJECT DeviceObject,
5691     PVOID Context
5692     )
5693 /*
5694     Routine Description:
5695 
5696     This function logs a provisioning type changed event to the system event log.
5697 
5698 Arguments:
5699     DeviceObject: The FDO that represents the device that reported the provisioning type change.
5700     Context: A pointer to the IO_WORKITEM in which this function is running.
5701 
5702 --*/
5703 {
5704     PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
5705 
5706     if (NT_SUCCESS(ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_PROVISIONING_TYPE_CHANGED))) {
5707 
5708         TracePrint((TRACE_LEVEL_INFORMATION,
5709                     TRACE_FLAG_GENERAL,
5710                     "ClassLogProvisioningTypeChangedEvent: DO (%p), LB Provisioning Type changed logged.\n",
5711                     DeviceObject));
5712     } else {
5713         TracePrint((TRACE_LEVEL_ERROR,
5714                     TRACE_FLAG_GENERAL,
5715                     "ClassLogProvisioningTypeChangedEvent: DO (%p), Failed to allocate memory for error log entry.\n",
5716                     DeviceObject));
5717     }
5718 
5719     ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
5720 
5721     IoFreeWorkItem(workItem);
5722 }
5723 
5724 
5725 VOID
ClassQueueProvisioningTypeChangedEventWorker(_In_ PDEVICE_OBJECT DeviceObject)5726 ClassQueueProvisioningTypeChangedEventWorker(
5727     _In_ PDEVICE_OBJECT DeviceObject
5728     )
5729 /*
5730 Routine Description:
5731 
5732     This function queues a delayed work item that will eventually log a
5733     provisioning type changed event to the system event log.
5734 
5735 Arguments:
5736     DeviceObject: The FDO that represents the device that reported the provisioning type change.
5737 
5738 --*/
5739 {
5740     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
5741     PIO_WORKITEM workItem = NULL;
5742 
5743     if (commonExtension->IsFdo)
5744     {
5745         workItem = IoAllocateWorkItem(DeviceObject);
5746 
5747         if (workItem)
5748         {
5749 
5750             TracePrint((TRACE_LEVEL_INFORMATION,
5751                         TRACE_FLAG_GENERAL,
5752                         "ClassQueueProvisioningTypeChangedEventWorker: DO (%p), Queueing LB provisioning type changed event work item.\n",
5753                         DeviceObject));
5754 
5755             ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
5756 
5757             //
5758             // Queue a work item to write the threshold notification to the
5759             // system event log.
5760             //
5761             IoQueueWorkItem(workItem, ClassLogProvisioningTypeChangedEvent, DelayedWorkQueue, workItem);
5762         }
5763         else
5764         {
5765             TracePrint((TRACE_LEVEL_ERROR,
5766                         TRACE_FLAG_GENERAL,
5767                         "ClassQueueProvisioningTypeChangedEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
5768                         DeviceObject));
5769         }
5770     }
5771 }
5772 
5773 _Function_class_(IO_WORKITEM_ROUTINE)
_IRQL_requires_(PASSIVE_LEVEL)5774 _IRQL_requires_(PASSIVE_LEVEL)
5775 _IRQL_requires_same_
5776 VOID
5777 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
5778 ClasspLogIOEventWithContext(
5779     _In_ PDEVICE_OBJECT DeviceObject,
5780     _In_opt_ PVOID Context
5781     )
5782 /*
5783     Routine Description:
5784 
5785     This function logs an event to the system event log with dumpdata containing opcode and
5786     sense data.
5787 
5788 Arguments:
5789     DeviceObject: The FDO that represents the device that retried the IO.
5790     Context: A pointer to the OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT that has data to be logged as part of the message.
5791 
5792 --*/
5793 {
5794     NTSTATUS status = STATUS_SUCCESS;
5795     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
5796     POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ioLogMessageContextHeader = (POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER)Context;
5797     PIO_WORKITEM workItem;
5798     PIO_ERROR_LOG_PACKET errorLogEntry = NULL;
5799     ULONG logEntrySize;
5800     PWCHAR stringIndex = NULL;
5801     LONG stringSize = 0;
5802     ULONG senseBufferSize;
5803     ULONG stringsBufferLength = 0;
5804     ULONG pdoNameLength = 0;
5805 
5806     NT_ASSERT(ioLogMessageContextHeader != NULL);
5807     _Analysis_assume_(ioLogMessageContextHeader != NULL);
5808 
5809     switch (ioLogMessageContextHeader->ErrorCode) {
5810 
5811         case IO_ERROR_IO_HARDWARE_ERROR:
5812         case IO_WARNING_IO_OPERATION_RETRIED: {
5813 
5814             //
5815             // We need to allocate enough space for 3 insertion strings:
5816             // 1. A ULONGLONG in Hex representing the LBA which means a max of 16 digits,
5817             // plus two for "0x" plus one for the NULL character.
5818             // 2. A ULONG representing the disk number in decimal, which means
5819             // a max of 10 digits, plus one for the NULL character.
5820             // 3. The PDO name, so that if the disk number is hidden from the
5821             // user for some reason, there is still a way to associate the
5822             // event with the correct device.
5823             //
5824             stringsBufferLength = (19 + 11) * sizeof(WCHAR);
5825 
5826             //
5827             // Query for the size of the PDO name.
5828             //
5829             status = IoGetDeviceProperty(fdoExtension->LowerPdo,
5830                                          DevicePropertyPhysicalDeviceObjectName,
5831                                          0,
5832                                          NULL,
5833                                          &pdoNameLength);
5834 
5835             if (status == STATUS_BUFFER_TOO_SMALL && pdoNameLength > 0) {
5836                 stringsBufferLength += pdoNameLength;
5837             } else {
5838                 pdoNameLength = 0;
5839             }
5840 
5841             break;
5842         }
5843 
5844     }
5845 
5846     workItem = ioLogMessageContextHeader->WorkItem;
5847 
5848     //
5849     // DumpData[0] which is of ULONG size and will contain opcode|srbstatus|scsistatus.
5850     // Then we will have sensebuffer, hence
5851     // DumpDataSize = senseBufferSize + sizeof(ULONG)
5852     // and DumpDataSize must be multiple of sizeof(ULONG)
5853     // which means senseBufferSize needs to ULONG aligned
5854     // Please note we will use original buffersize for padding later
5855     //
5856     senseBufferSize = ALIGN_UP_BY(ioLogMessageContextHeader->SenseDataSize, sizeof(ULONG));
5857 
5858     logEntrySize = FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + sizeof(ULONG) + senseBufferSize;
5859 
5860     //
5861     // We need to make sure the string offset is WCHAR-aligned (the insertion strings
5862     // come after the sense buffer in the dump data, if any).
5863     // But we don't need to do anything special for it,
5864     // since FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData) is currently ULONG aligned
5865     // and SenseBufferSize is also ULONG aligned. This means buffer that precedes the insertion string is ULONG aligned
5866     // note stringoffset = FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + DumpDataSize
5867     // This leads us to fact that stringoffset will always be ULONG aligned and effectively WCHAR aligned
5868     //
5869 
5870     //
5871     // We need to allocate enough space for the insertion strings provided in the passed in Context
5872     // as well as the opcode and the sense data, while making sure we cap at max error log size.
5873     // The log packet is followed by the opcode, then the sense data, and then the
5874     // insertion strings.
5875     //
5876     logEntrySize = logEntrySize + stringsBufferLength;
5877 
5878     if (logEntrySize > ERROR_LOG_MAXIMUM_SIZE) {
5879         if (senseBufferSize) {
5880             if (logEntrySize - ERROR_LOG_MAXIMUM_SIZE < senseBufferSize) {
5881                 //
5882                 // In below steps, senseBufferSize will become same or less than as ioLogMessageContextHeader->SenseDataSize
5883                 // it can't be more than that.
5884                 //
5885                 senseBufferSize -= logEntrySize - ERROR_LOG_MAXIMUM_SIZE;
5886 
5887                 //
5888                 // Decrease the sensebuffersize further, if needed, to keep senseBufferSize ULONG aligned
5889                 //
5890                 senseBufferSize = ALIGN_DOWN_BY(senseBufferSize, sizeof(ULONG));
5891 
5892             } else {
5893                 senseBufferSize = 0;
5894             }
5895         }
5896         logEntrySize = ERROR_LOG_MAXIMUM_SIZE;
5897     }
5898 
5899     errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceObject, (UCHAR)logEntrySize);
5900 
5901     if (errorLogEntry) {
5902 
5903         RtlZeroMemory(errorLogEntry, logEntrySize);
5904         errorLogEntry->MajorFunctionCode = IRP_MJ_SCSI;
5905         errorLogEntry->RetryCount = 1;
5906         errorLogEntry->DumpDataSize = (USHORT)(sizeof(ULONG) + senseBufferSize);
5907         errorLogEntry->StringOffset = (USHORT)(FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + errorLogEntry->DumpDataSize);
5908         errorLogEntry->ErrorCode = ioLogMessageContextHeader->ErrorCode;
5909         errorLogEntry->DumpData[0] = (((ULONG)(ioLogMessageContextHeader->OpCode)) << 24) |
5910                                      (((ULONG)(ioLogMessageContextHeader->SrbStatus)) << 16) |
5911                                      (((ULONG)(ioLogMessageContextHeader->ScsiStatus)) << 8);
5912 
5913         //
5914         // Copy sense data and do padding for sense data if needed, with '-'
5915         //
5916         if (senseBufferSize  > ioLogMessageContextHeader->SenseDataSize) {
5917             RtlCopyMemory(&errorLogEntry->DumpData[1], ioLogMessageContextHeader->SenseData, ioLogMessageContextHeader->SenseDataSize);
5918             RtlFillMemory( (PCHAR)&errorLogEntry->DumpData[1] + ioLogMessageContextHeader->SenseDataSize , (senseBufferSize - ioLogMessageContextHeader->SenseDataSize) , '-' );
5919         } else  {
5920             RtlCopyMemory(&errorLogEntry->DumpData[1], ioLogMessageContextHeader->SenseData, senseBufferSize);
5921         }
5922 
5923         stringIndex = (PWCHAR)((PCHAR)errorLogEntry->DumpData + errorLogEntry->DumpDataSize);
5924         stringSize = logEntrySize - errorLogEntry->StringOffset;
5925 
5926         //
5927         // Add the strings
5928         //
5929         switch (ioLogMessageContextHeader->ErrorCode) {
5930             case IO_ERROR_IO_HARDWARE_ERROR:
5931             case IO_WARNING_IO_OPERATION_RETRIED: {
5932 
5933                 PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = (PIO_RETRIED_LOG_MESSAGE_CONTEXT)Context;
5934 
5935                 //
5936                 // The first is a "0x" plus ULONGLONG in hex representing the LBA plus the NULL character.
5937                 // The second is a ULONG representing the disk number plus the NULL character.
5938                 //
5939                 status = RtlStringCbPrintfW(stringIndex, stringSize, L"0x%I64x", ioLogMessageContext->Lba.QuadPart);
5940                 if (NT_SUCCESS(status)) {
5941                     errorLogEntry->NumberOfStrings++;
5942 
5943                     //
5944                     // Add the disk number to the insertion strings.
5945                     //
5946                     stringSize -= (ULONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
5947                     stringIndex += (wcslen(stringIndex) + 1);
5948 
5949                     if (stringSize > 0) {
5950 
5951                         status = RtlStringCbPrintfW(stringIndex, stringSize, L"%d", ioLogMessageContext->DeviceNumber);
5952 
5953                         if (NT_SUCCESS(status)) {
5954 
5955                             errorLogEntry->NumberOfStrings++;
5956 
5957                             stringSize -= (ULONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
5958                             stringIndex += (wcslen(stringIndex) + 1);
5959 
5960                             if (stringSize >= (LONG)pdoNameLength && pdoNameLength > 0) {
5961                                 ULONG resultLength;
5962 
5963                                 //
5964                                 // Get the PDO name and place it in the insertion string buffer.
5965                                 //
5966                                 status = IoGetDeviceProperty(fdoExtension->LowerPdo,
5967                                                              DevicePropertyPhysicalDeviceObjectName,
5968                                                              pdoNameLength,
5969                                                              stringIndex,
5970                                                              &resultLength);
5971 
5972                                 if (NT_SUCCESS(status) && resultLength > 0) {
5973                                     errorLogEntry->NumberOfStrings++;
5974                                 }
5975                             }
5976                         }
5977                     }
5978                 }
5979 
5980                 break;
5981             }
5982 
5983         }
5984 
5985         //
5986         // Write the error log packet to the system error logging thread.
5987         // It will be freed automatically.
5988         //
5989         IoWriteErrorLogEntry(errorLogEntry);
5990 
5991         TracePrint((TRACE_LEVEL_INFORMATION,
5992                     TRACE_FLAG_GENERAL,
5993                     "ClasspLogIORetriedEvent: DO (%p), Soft threshold notification logged.\n",
5994                     DeviceObject));
5995     }
5996 
5997     ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
5998 
5999     if (ioLogMessageContextHeader->SenseData) {
6000         ExFreePool(ioLogMessageContextHeader->SenseData);
6001     }
6002     if (workItem) {
6003         IoFreeWorkItem(workItem);
6004     }
6005     ExFreePool(ioLogMessageContextHeader);
6006 }
6007 
6008 
6009 VOID
ClasspQueueLogIOEventWithContextWorker(_In_ PDEVICE_OBJECT DeviceObject,_In_ ULONG SenseBufferSize,_In_ PVOID SenseData,_In_ UCHAR SrbStatus,_In_ UCHAR ScsiStatus,_In_ ULONG ErrorCode,_In_ ULONG CdbLength,_In_opt_ PCDB Cdb,_In_opt_ PTRANSFER_PACKET Pkt)6010 ClasspQueueLogIOEventWithContextWorker(
6011     _In_ PDEVICE_OBJECT DeviceObject,
6012     _In_ ULONG SenseBufferSize,
6013     _In_ PVOID SenseData,
6014     _In_ UCHAR SrbStatus,
6015     _In_ UCHAR ScsiStatus,
6016     _In_ ULONG ErrorCode,
6017     _In_ ULONG CdbLength,
6018     _In_opt_ PCDB Cdb,
6019     _In_opt_ PTRANSFER_PACKET Pkt
6020     )
6021 /*
6022 Routine Description:
6023 
6024     Helper function that queues a delayed work item that will eventually
6025     log an event to the system event log corresponding to passed in ErrorCode.
6026     The dumpdata is fixed to include the opcode and the sense information.
6027     But the number of insertion strings varies based on the passed in ErrorCode.
6028 
6029 Arguments:
6030     DeviceObject: The FDO that represents the device that was the target of the IO.
6031     SesneBufferSize: Size of the SenseData buffer.
6032     SenseData: Error information from the target (to be included in the dump data).
6033     SrbStatus: Srb status returned by the miniport.
6034     ScsiStatus: SCSI status associated with the request upon completion from lower layers.
6035     ErrorCode: Numerical value of the error code.
6036     CdbLength: Number of bytes of Cdb.
6037     Cdb: Pointer to the CDB.
6038     Pkt: The tranfer packet representing the IO of interest. This may be NULL.
6039 
6040 --*/
6041 {
6042     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
6043     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
6044     POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ioLogMessageContextHeader = NULL;
6045     PVOID senseData = NULL;
6046     PIO_WORKITEM workItem = NULL;
6047     ULONG senseBufferSize = 0;
6048     LARGE_INTEGER lba = {0};
6049 
6050     if (!commonExtension->IsFdo) {
6051         return;
6052     }
6053 
6054     if (!Cdb) {
6055         return;
6056     }
6057 
6058     workItem = IoAllocateWorkItem(DeviceObject);
6059     if (!workItem) {
6060         goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage;
6061     }
6062 
6063     if (SenseBufferSize) {
6064         senseData = ExAllocatePoolWithTag(NonPagedPoolNx, SenseBufferSize, CLASSPNP_POOL_TAG_LOG_MESSAGE);
6065         if (senseData) {
6066             senseBufferSize = SenseBufferSize;
6067         }
6068     }
6069 
6070     if (CdbLength == 16) {
6071         REVERSE_BYTES_QUAD(&lba, Cdb->CDB16.LogicalBlock);
6072     } else {
6073         ((PFOUR_BYTE)&lba.LowPart)->Byte3 = Cdb->CDB10.LogicalBlockByte0;
6074         ((PFOUR_BYTE)&lba.LowPart)->Byte2 = Cdb->CDB10.LogicalBlockByte1;
6075         ((PFOUR_BYTE)&lba.LowPart)->Byte1 = Cdb->CDB10.LogicalBlockByte2;
6076         ((PFOUR_BYTE)&lba.LowPart)->Byte0 = Cdb->CDB10.LogicalBlockByte3;
6077     }
6078 
6079     //
6080     // Calculate the amount of buffer required for the insertion strings.
6081     //
6082     switch (ErrorCode) {
6083         case IO_ERROR_IO_HARDWARE_ERROR:
6084         case IO_WARNING_IO_OPERATION_RETRIED: {
6085 
6086             PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = NULL;
6087 
6088             ioLogMessageContext = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(IO_RETRIED_LOG_MESSAGE_CONTEXT), CLASSPNP_POOL_TAG_LOG_MESSAGE);
6089             if (!ioLogMessageContext) {
6090                 goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage;
6091             }
6092 
6093             ioLogMessageContext->Lba.QuadPart = lba.QuadPart;
6094             ioLogMessageContext->DeviceNumber = fdoExtension->DeviceNumber;
6095 
6096             ioLogMessageContextHeader = (POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER)ioLogMessageContext;
6097 
6098             break;
6099         }
6100 
6101         default: goto __ClasspQueueLogIOEventWithContextWorker_Exit;
6102     }
6103 
6104     TracePrint((TRACE_LEVEL_INFORMATION,
6105                 TRACE_FLAG_GENERAL,
6106                 "ClasspQueueLogIOEventWithContextWorker: DO (%p), Pkt (%p), Queueing IO retried event log message work item.\n",
6107                 DeviceObject,
6108                 Pkt));
6109 
6110     ioLogMessageContextHeader->WorkItem = workItem;
6111     if (senseData) {
6112         RtlCopyMemory(senseData, SenseData, SenseBufferSize);
6113     }
6114     ioLogMessageContextHeader->SenseData = senseData;
6115     ioLogMessageContextHeader->SenseDataSize = senseBufferSize;
6116     ioLogMessageContextHeader->SrbStatus = SrbStatus;
6117     ioLogMessageContextHeader->ScsiStatus = ScsiStatus;
6118     ioLogMessageContextHeader->OpCode = Cdb->CDB6GENERIC.OperationCode;
6119     ioLogMessageContextHeader->Reserved = 0;
6120     ioLogMessageContextHeader->ErrorCode = ErrorCode;
6121 
6122     ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
6123 
6124     //
6125     // Queue a work item to write the system event log.
6126     //
6127     IoQueueWorkItem(workItem, ClasspLogIOEventWithContext, DelayedWorkQueue, ioLogMessageContextHeader);
6128 
6129     return;
6130 
6131 __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage:
6132 
6133     TracePrint((TRACE_LEVEL_ERROR,
6134                 TRACE_FLAG_GENERAL,
6135                 "ClasspQueueLogIOEventWithContextWorker: DO (%p), Failed to allocate memory for the log message.\n",
6136                 DeviceObject));
6137 
6138 __ClasspQueueLogIOEventWithContextWorker_Exit:
6139     if (senseData) {
6140         ExFreePool(senseData);
6141     }
6142     if (workItem) {
6143         IoFreeWorkItem(workItem);
6144     }
6145     if (ioLogMessageContextHeader) {
6146         ExFreePool(ioLogMessageContextHeader);
6147     }
6148 }
6149 
6150 static
6151 BOOLEAN
ValidPersistentReserveScope(UCHAR Scope)6152 ValidPersistentReserveScope(
6153     UCHAR Scope)
6154 {
6155     switch (Scope) {
6156     case RESERVATION_SCOPE_LU:
6157     case RESERVATION_SCOPE_ELEMENT:
6158 
6159         return TRUE;
6160 
6161     default:
6162 
6163         break;
6164     }
6165 
6166     return FALSE;
6167 }
6168 
6169 static
6170 BOOLEAN
ValidPersistentReserveType(UCHAR Type)6171 ValidPersistentReserveType(
6172     UCHAR Type)
6173 {
6174     switch (Type) {
6175     case RESERVATION_TYPE_WRITE_EXCLUSIVE:
6176     case RESERVATION_TYPE_EXCLUSIVE:
6177     case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS:
6178     case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS:
6179 
6180         return TRUE;
6181 
6182     default:
6183 
6184         break;
6185     }
6186 
6187     return FALSE;
6188 }
6189 
6190 
6191 /*++
6192 
6193 ClasspPersistentReserve
6194 
6195 Routine Description:
6196 
6197     Handles IOCTL_STORAGE_PERSISTENT_RESERVE_IN and IOCTL_STORAGE_PERSISTENT_RESERVE_OUT.
6198 
6199 Arguments:
6200 
6201     DeviceObject - a pointer to the device object
6202     Irp - a pointer to the I/O request packet
6203     Srb - pointer to preallocated SCSI_REQUEST_BLOCK.
6204 
6205 Return Value:
6206 
6207     Status Code
6208 
6209 --*/
6210 NTSTATUS
ClasspPersistentReserve(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)6211 ClasspPersistentReserve(
6212     _In_ PDEVICE_OBJECT DeviceObject,
6213     _In_ PIRP Irp,
6214     _Inout_ PSCSI_REQUEST_BLOCK Srb
6215     )
6216 {
6217     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6218     PCDB cdb = NULL;
6219     PPERSISTENT_RESERVE_COMMAND prCommand = Irp->AssociatedIrp.SystemBuffer;
6220     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
6221 
6222     NTSTATUS status;
6223 
6224     ULONG dataBufLen;
6225     ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
6226 
6227     BOOLEAN writeToDevice;
6228 
6229     //
6230     // Check common input buffer parameters.
6231     //
6232 
6233     if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
6234             sizeof(PERSISTENT_RESERVE_COMMAND) ||
6235         prCommand->Size < sizeof(PERSISTENT_RESERVE_COMMAND)) {
6236 
6237         status = STATUS_INFO_LENGTH_MISMATCH;
6238         Irp->IoStatus.Status = status;
6239         Irp->IoStatus.Information = 0;
6240 
6241         FREE_POOL(Srb);
6242 
6243         ClassReleaseRemoveLock(DeviceObject, Irp);
6244         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6245         goto ClasspPersistentReserve_Exit;
6246     }
6247 
6248     //
6249     // Check buffer alignment. Only an issue if another kernel mode component
6250     // (not the I/O manager) allocates the buffer.
6251     //
6252 
6253     if ((ULONG_PTR)prCommand & fdoExtension->AdapterDescriptor->AlignmentMask) {
6254 
6255         status = STATUS_INVALID_USER_BUFFER;
6256         Irp->IoStatus.Status = status;
6257         Irp->IoStatus.Information = 0;
6258 
6259         FREE_POOL(Srb);
6260 
6261         ClassReleaseRemoveLock(DeviceObject, Irp);
6262         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6263         goto ClasspPersistentReserve_Exit;
6264     }
6265 
6266     //
6267     // Check additional parameters.
6268     //
6269 
6270     status = STATUS_SUCCESS;
6271 
6272     SrbSetCdbLength(Srb, 10);
6273     cdb = SrbGetCdb(Srb);
6274 
6275     if (controlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN) {
6276 
6277         //
6278         // Check output buffer for PR In.
6279         //
6280 
6281         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
6282             prCommand->PR_IN.AllocationLength) {
6283 
6284             status = STATUS_INVALID_PARAMETER;
6285         }
6286 
6287         switch (prCommand->PR_IN.ServiceAction) {
6288 
6289         case RESERVATION_ACTION_READ_KEYS:
6290 
6291             if (prCommand->PR_IN.AllocationLength < sizeof(PRI_REGISTRATION_LIST)) {
6292 
6293                 status = STATUS_INVALID_PARAMETER;
6294             }
6295 
6296             break;
6297 
6298         case RESERVATION_ACTION_READ_RESERVATIONS:
6299 
6300             if (prCommand->PR_IN.AllocationLength < sizeof(PRI_RESERVATION_LIST)) {
6301 
6302                 status = STATUS_INVALID_PARAMETER;
6303             }
6304 
6305             break;
6306 
6307         default:
6308 
6309             status = STATUS_INVALID_PARAMETER;
6310             break;
6311         }
6312 
6313         if (!NT_SUCCESS(status)) {
6314 
6315             Irp->IoStatus.Status = status;
6316             Irp->IoStatus.Information = 0;
6317 
6318             FREE_POOL(Srb);
6319 
6320             ClassReleaseRemoveLock(DeviceObject, Irp);
6321             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6322             goto ClasspPersistentReserve_Exit;
6323         }
6324 
6325         //
6326         // Fill in the CDB.
6327         //
6328 
6329         cdb->PERSISTENT_RESERVE_IN.OperationCode    = SCSIOP_PERSISTENT_RESERVE_IN;
6330         cdb->PERSISTENT_RESERVE_IN.ServiceAction    = prCommand->PR_IN.ServiceAction;
6331 
6332         REVERSE_BYTES_SHORT(&(cdb->PERSISTENT_RESERVE_IN.AllocationLength),
6333                             &(prCommand->PR_IN.AllocationLength));
6334 
6335         dataBufLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
6336         writeToDevice = FALSE;
6337 
6338 
6339     } else {
6340 
6341         //
6342         // Verify ServiceAction, Scope, and Type
6343         //
6344 
6345         switch (prCommand->PR_OUT.ServiceAction) {
6346 
6347         case RESERVATION_ACTION_REGISTER:
6348         case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING:
6349         case RESERVATION_ACTION_CLEAR:
6350 
6351             // Scope and type ignored.
6352 
6353             break;
6354 
6355         case RESERVATION_ACTION_RESERVE:
6356         case RESERVATION_ACTION_RELEASE:
6357         case RESERVATION_ACTION_PREEMPT:
6358         case RESERVATION_ACTION_PREEMPT_ABORT:
6359 
6360             if (!ValidPersistentReserveScope(prCommand->PR_OUT.Scope) ||
6361                 !ValidPersistentReserveType(prCommand->PR_OUT.Type)) {
6362 
6363                 status = STATUS_INVALID_PARAMETER;
6364 
6365             }
6366 
6367             break;
6368 
6369         default:
6370 
6371             status = STATUS_INVALID_PARAMETER;
6372 
6373             break;
6374         }
6375 
6376         //
6377         // Check input buffer for PR Out.
6378         // Caller must include the PR parameter list.
6379         //
6380 
6381         if (NT_SUCCESS(status)) {
6382 
6383             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
6384                     (sizeof(PERSISTENT_RESERVE_COMMAND) +
6385                      sizeof(PRO_PARAMETER_LIST)) ||
6386                 prCommand->Size <
6387                     irpStack->Parameters.DeviceIoControl.InputBufferLength) {
6388 
6389             status = STATUS_INVALID_PARAMETER;
6390 
6391             }
6392         }
6393 
6394 
6395         if (!NT_SUCCESS(status)) {
6396 
6397             Irp->IoStatus.Status = status;
6398             Irp->IoStatus.Information = 0;
6399 
6400             FREE_POOL(Srb);
6401 
6402             ClassReleaseRemoveLock(DeviceObject, Irp);
6403             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6404             goto ClasspPersistentReserve_Exit;
6405         }
6406 
6407         //
6408         // Fill in the CDB.
6409         //
6410 
6411         cdb->PERSISTENT_RESERVE_OUT.OperationCode   = SCSIOP_PERSISTENT_RESERVE_OUT;
6412         cdb->PERSISTENT_RESERVE_OUT.ServiceAction   = prCommand->PR_OUT.ServiceAction;
6413         cdb->PERSISTENT_RESERVE_OUT.Scope           = prCommand->PR_OUT.Scope;
6414         cdb->PERSISTENT_RESERVE_OUT.Type            = prCommand->PR_OUT.Type;
6415 
6416         cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = (UCHAR)sizeof(PRO_PARAMETER_LIST);
6417 
6418         //
6419         // Move the parameter list to the beginning of the data buffer (so it is aligned
6420         // correctly and that the MDL describes it correctly).
6421         //
6422 
6423         RtlMoveMemory(prCommand,
6424                       prCommand->PR_OUT.ParameterList,
6425                       sizeof(PRO_PARAMETER_LIST));
6426 
6427         dataBufLen = sizeof(PRO_PARAMETER_LIST);
6428         writeToDevice = TRUE;
6429     }
6430 
6431     //
6432     // Fill in the SRB
6433     //
6434 
6435     //
6436     // Set timeout value.
6437     //
6438 
6439     SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
6440 
6441     //
6442     // Send as a tagged request.
6443     //
6444 
6445     SrbSetRequestAttribute(Srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
6446     SrbSetSrbFlags(Srb, SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_QUEUE_ACTION_ENABLE);
6447 
6448     status = ClassSendSrbAsynchronous(DeviceObject,
6449                                       Srb,
6450                                       Irp,
6451                                       prCommand,
6452                                       dataBufLen,
6453                                       writeToDevice);
6454 
6455 ClasspPersistentReserve_Exit:
6456 
6457     return status;
6458 
6459 }
6460 
6461 /*++
6462 
6463 ClasspPriorityHint
6464 
6465 Routine Description:
6466 
6467     Handles IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT.
6468 
6469 Arguments:
6470 
6471     DeviceObject - a pointer to the device object
6472     Irp - a pointer to the I/O request packet
6473 
6474 Return Value:
6475 
6476     Status Code
6477 
6478 --*/
6479 NTSTATUS
ClasspPriorityHint(PDEVICE_OBJECT DeviceObject,PIRP Irp)6480 ClasspPriorityHint(
6481     PDEVICE_OBJECT DeviceObject,
6482     PIRP Irp
6483     )
6484 {
6485     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
6486     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6487     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
6488     PSTORAGE_PRIORITY_HINT_SUPPORT priSupport = Irp->AssociatedIrp.SystemBuffer;
6489     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6490     NTSTATUS status = STATUS_SUCCESS;
6491 
6492     Irp->IoStatus.Information = 0;
6493 
6494     //
6495     // Check whether this device supports idle priority.
6496     //
6497     if (!fdoData->IdlePrioritySupported) {
6498         status = STATUS_NOT_SUPPORTED;
6499         goto PriorityHintExit;
6500     }
6501 
6502     if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
6503         sizeof(STORAGE_PRIORITY_HINT_SUPPORT)) {
6504 
6505         status = STATUS_BUFFER_TOO_SMALL;
6506         goto PriorityHintExit;
6507     }
6508 
6509     RtlZeroMemory(priSupport, sizeof(STORAGE_PRIORITY_HINT_SUPPORT));
6510 
6511     status = ClassForwardIrpSynchronous(commonExtension, Irp);
6512     if (!NT_SUCCESS(status)) {
6513         //
6514         // If I/O priority is not supported by lower drivers, just set the
6515         // priorities supported by class driver.
6516         //
6517         TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL, "ClasspPriorityHint: I/O priority not supported by port driver.\n"));
6518         priSupport->SupportFlags = 0;
6519         status = STATUS_SUCCESS;
6520     }
6521 
6522     TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL, "ClasspPriorityHint: I/O priorities supported by port driver: %X\n", priSupport->SupportFlags));
6523 
6524     priSupport->SupportFlags |= (1 << IoPriorityVeryLow) |
6525                                 (1 << IoPriorityLow) |
6526                                 (1 << IoPriorityNormal) ;
6527 
6528     TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL, "ClasspPriorityHint: I/O priorities supported: %X\n", priSupport->SupportFlags));
6529     Irp->IoStatus.Information = sizeof(STORAGE_PRIORITY_HINT_SUPPORT);
6530 
6531 PriorityHintExit:
6532 
6533     Irp->IoStatus.Status = status;
6534     ClassReleaseRemoveLock(DeviceObject, Irp);
6535     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6536     return status;
6537 }
6538 
6539 /*++
6540 
6541 ClasspConvertToScsiRequestBlock
6542 
6543 Routine Description:
6544 
6545     Convert an extended SRB to a SCSI_REQUEST_BLOCK. This function handles only
6546     a single SRB and will not converted SRBs that are linked.
6547 
6548 Arguments:
6549 
6550     Srb - a pointer to a SCSI_REQUEST_BLOCK
6551     SrbEx - a pointer to an extended SRB
6552 
6553 Return Value:
6554 
6555     None
6556 
6557 --*/
6558 VOID
ClasspConvertToScsiRequestBlock(_Out_ PSCSI_REQUEST_BLOCK Srb,_In_ PSTORAGE_REQUEST_BLOCK SrbEx)6559 ClasspConvertToScsiRequestBlock(
6560     _Out_ PSCSI_REQUEST_BLOCK Srb,
6561     _In_ PSTORAGE_REQUEST_BLOCK SrbEx
6562     )
6563 {
6564     PSTOR_ADDR_BTL8 storAddrBtl8;
6565     ULONG i;
6566     BOOLEAN foundEntry = FALSE;
6567     PSRBEX_DATA srbExData;
6568 
6569     if ((Srb == NULL) || (SrbEx == NULL)) {
6570         return;
6571     }
6572 
6573     RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
6574 
6575     Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
6576     Srb->Function = (UCHAR)SrbEx->SrbFunction;
6577     Srb->SrbStatus = SrbEx->SrbStatus;
6578     Srb->QueueTag = (UCHAR)SrbEx->RequestTag;
6579     Srb->QueueAction = (UCHAR)SrbEx->RequestAttribute;
6580     Srb->SrbFlags = SrbEx->SrbFlags;
6581     Srb->DataTransferLength = SrbEx->DataTransferLength;
6582     Srb->TimeOutValue = SrbEx->TimeOutValue;
6583     Srb->DataBuffer = SrbEx->DataBuffer;
6584     Srb->OriginalRequest = SrbEx->OriginalRequest;
6585     Srb->SrbExtension = SrbEx->MiniportContext;
6586     Srb->InternalStatus = SrbEx->SystemStatus;
6587 
6588     //
6589     // Handle address fields
6590     //
6591     if (SrbEx->AddressOffset >= sizeof(STORAGE_REQUEST_BLOCK)) {
6592         storAddrBtl8 = (PSTOR_ADDR_BTL8)((PCHAR)SrbEx + SrbEx->AddressOffset);
6593 
6594         if (storAddrBtl8->Type == STOR_ADDRESS_TYPE_BTL8) {
6595             Srb->PathId = storAddrBtl8->Path;
6596             Srb->TargetId = storAddrBtl8->Target;
6597             Srb->Lun = storAddrBtl8->Lun;
6598         } else {
6599             // Catch unsupported address types
6600             NT_ASSERT(FALSE);
6601         }
6602     }
6603 
6604     //
6605     // Handle SRB function specific fields
6606     //
6607     if (SrbEx->NumSrbExData > 0) {
6608 
6609         for (i = 0; i < SrbEx->NumSrbExData; i++) {
6610 
6611             if ((SrbEx->SrbExDataOffset[i] == 0) ||
6612                 (SrbEx->SrbExDataOffset[i] < sizeof(STORAGE_REQUEST_BLOCK))) {
6613                 // Catch invalid offsets
6614                 NT_ASSERT(FALSE);
6615                 continue;
6616             }
6617 
6618             srbExData = (PSRBEX_DATA)((PCHAR)SrbEx + SrbEx->SrbExDataOffset[i]);
6619 
6620             switch (SrbEx->SrbFunction) {
6621 
6622                 case SRB_FUNCTION_EXECUTE_SCSI:
6623 
6624                     switch (srbExData->Type) {
6625 
6626                         case SrbExDataTypeScsiCdb16:
6627                             Srb->ScsiStatus = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->ScsiStatus;
6628                             Srb->CdbLength = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->CdbLength;
6629                             Srb->SenseInfoBufferLength = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->SenseInfoBufferLength;
6630                             Srb->SenseInfoBuffer = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->SenseInfoBuffer;
6631                             RtlCopyMemory(Srb->Cdb, ((PSRBEX_DATA_SCSI_CDB16)srbExData)->Cdb, sizeof(Srb->Cdb));
6632                             foundEntry = TRUE;
6633                             break;
6634 
6635                         case SrbExDataTypeScsiCdb32:
6636                             Srb->ScsiStatus = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->ScsiStatus;
6637                             Srb->CdbLength = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->CdbLength;
6638                             Srb->SenseInfoBufferLength = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->SenseInfoBufferLength;
6639                             Srb->SenseInfoBuffer = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->SenseInfoBuffer;
6640 
6641                             // Copy only the first 16 bytes
6642                             RtlCopyMemory(Srb->Cdb, ((PSRBEX_DATA_SCSI_CDB32)srbExData)->Cdb, sizeof(Srb->Cdb));
6643                             foundEntry = TRUE;
6644                             break;
6645 
6646                         case SrbExDataTypeScsiCdbVar:
6647                             Srb->ScsiStatus = ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->ScsiStatus;
6648                             Srb->CdbLength = (UCHAR)((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->CdbLength;
6649                             Srb->SenseInfoBufferLength = ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->SenseInfoBufferLength;
6650                             Srb->SenseInfoBuffer = ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->SenseInfoBuffer;
6651 
6652                             // Copy only the first 16 bytes
6653                             RtlCopyMemory(Srb->Cdb, ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->Cdb, sizeof(Srb->Cdb));
6654                             foundEntry = TRUE;
6655                             break;
6656 
6657                         default:
6658                             break;
6659 
6660                     }
6661                     break;
6662 
6663                 case SRB_FUNCTION_WMI:
6664 
6665                     if (srbExData->Type == SrbExDataTypeWmi) {
6666                         ((PSCSI_WMI_REQUEST_BLOCK)Srb)->WMISubFunction = ((PSRBEX_DATA_WMI)srbExData)->WMISubFunction;
6667                         ((PSCSI_WMI_REQUEST_BLOCK)Srb)->WMIFlags = ((PSRBEX_DATA_WMI)srbExData)->WMIFlags;
6668                         ((PSCSI_WMI_REQUEST_BLOCK)Srb)->DataPath = ((PSRBEX_DATA_WMI)srbExData)->DataPath;
6669                         foundEntry = TRUE;
6670                     }
6671                     break;
6672 
6673                 case SRB_FUNCTION_PNP:
6674 
6675                     if (srbExData->Type == SrbExDataTypePnP) {
6676                         ((PSCSI_PNP_REQUEST_BLOCK)Srb)->PnPAction = ((PSRBEX_DATA_PNP)srbExData)->PnPAction;
6677                         ((PSCSI_PNP_REQUEST_BLOCK)Srb)->PnPSubFunction = ((PSRBEX_DATA_PNP)srbExData)->PnPSubFunction;
6678                         ((PSCSI_PNP_REQUEST_BLOCK)Srb)->SrbPnPFlags = ((PSRBEX_DATA_PNP)srbExData)->SrbPnPFlags;
6679                         foundEntry = TRUE;
6680                     }
6681                     break;
6682 
6683                 case SRB_FUNCTION_POWER:
6684 
6685                     if (srbExData->Type == SrbExDataTypePower) {
6686                         ((PSCSI_POWER_REQUEST_BLOCK)Srb)->DevicePowerState = ((PSRBEX_DATA_POWER)srbExData)->DevicePowerState;
6687                         ((PSCSI_POWER_REQUEST_BLOCK)Srb)->PowerAction = ((PSRBEX_DATA_POWER)srbExData)->PowerAction;
6688                         ((PSCSI_POWER_REQUEST_BLOCK)Srb)->SrbPowerFlags = ((PSRBEX_DATA_POWER)srbExData)->SrbPowerFlags;
6689                         foundEntry = TRUE;
6690                     }
6691                     break;
6692 
6693                 default:
6694                     break;
6695 
6696             }
6697 
6698             //
6699             // Quit on first match
6700             //
6701             if (foundEntry) {
6702                 break;
6703             }
6704         }
6705     }
6706 
6707     return;
6708 }
6709 
6710 
6711 
_IRQL_requires_max_(PASSIVE_LEVEL)6712 _IRQL_requires_max_(PASSIVE_LEVEL)
6713 NTSTATUS
6714 ClasspGetMaximumTokenListIdentifier(
6715     _In_ PDEVICE_OBJECT DeviceObject,
6716     _In_z_ PWSTR RegistryPath,
6717     _Out_ PULONG MaximumListIdentifier
6718     )
6719 
6720 /*++
6721 
6722 Routine Description:
6723 
6724     This routine returns the maximum ListIdentifier (to be used when building TokenOperation
6725     requests) by querying the value MaximumListIdentifier under the key 'RegistryPath'.
6726 
6727 Arguments:
6728 
6729     DeviceObject - The device handling the request.
6730     RegistryPath - The absolute registry path under which MaximumListIdentifier resides.
6731     MaximumListIdentifier - Returns the value being queried.
6732 
6733 Return Value:
6734 
6735     STATUS_SUCCESS or appropriate error status returned by Registry API.
6736 
6737 --*/
6738 
6739 {
6740     RTL_QUERY_REGISTRY_TABLE queryTable[2];
6741     ULONG value = 0;
6742     NTSTATUS status;
6743 
6744     TracePrint((TRACE_LEVEL_VERBOSE,
6745                 TRACE_FLAG_PNP,
6746                 "ClasspGetMaximumTokenListIdentifier (%p): Entering function.\n",
6747                 DeviceObject));
6748 
6749     //
6750     // Zero the table entries.
6751     //
6752     RtlZeroMemory(queryTable, sizeof(queryTable));
6753 
6754     //
6755     // The query table has two entries. One for the MaximumListIdentifier and
6756     // the second which is the 'NULL' terminator.
6757     //
6758     // Indicate that there is NO call-back routine.
6759     //
6760     queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
6761 
6762     //
6763     // The value to query.
6764     //
6765     queryTable[0].Name = REG_MAX_LIST_IDENTIFIER_VALUE;
6766 
6767     //
6768     // Where to put the value, the type of the value, default value and length.
6769     //
6770     queryTable[0].EntryContext = &value;
6771     queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
6772     queryTable[0].DefaultData = &value;
6773     queryTable[0].DefaultLength = sizeof(value);
6774 
6775     //
6776     // Try to get the maximum listIdentifier.
6777     //
6778     status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
6779                                     RegistryPath,
6780                                     queryTable,
6781                                     NULL,
6782                                     NULL);
6783 
6784     if (NT_SUCCESS(status)) {
6785         *MaximumListIdentifier = value;
6786     } else {
6787     *MaximumListIdentifier = 0;
6788     }
6789 
6790     TracePrint((TRACE_LEVEL_VERBOSE,
6791                 TRACE_FLAG_PNP,
6792                 "ClasspGetMaximumTokenListIdentifier (%p): Exiting function with status %x (maxListId %u).\n",
6793                 DeviceObject,
6794                 status,
6795                 *MaximumListIdentifier));
6796 
6797     return status;
6798 }
6799 
_IRQL_requires_max_(PASSIVE_LEVEL)6800 _IRQL_requires_max_(PASSIVE_LEVEL)
6801 NTSTATUS
6802 ClasspGetCopyOffloadMaxDuration(
6803     _In_ PDEVICE_OBJECT DeviceObject,
6804     _In_z_ PWSTR RegistryPath,
6805     _Out_ PULONG MaxDuration
6806     )
6807 
6808     /*++
6809 
6810     Routine Description:
6811 
6812     This routine returns the maximum time (in seconds) that a Copy Offload
6813     operation should take to complete by a target.
6814 
6815     Arguments:
6816 
6817     DeviceObject - The device handling the request.
6818     RegistryPath - The absolute registry path under which MaxDuration resides.
6819     MaxDuration - Returns the value being queried, in seconds.
6820 
6821     Return Value:
6822 
6823     STATUS_SUCCESS or appropriate error status returned by Registry API.
6824 
6825     --*/
6826 
6827 {
6828     RTL_QUERY_REGISTRY_TABLE queryTable[2];
6829     ULONG value = 0;
6830     NTSTATUS status;
6831 
6832     TracePrint((TRACE_LEVEL_VERBOSE,
6833                 TRACE_FLAG_PNP,
6834                 "ClasspGetCopyOffloadMaxDuration (%p): Entering function.\n",
6835                 DeviceObject));
6836 
6837     //
6838     // Zero the table entries.
6839     //
6840     RtlZeroMemory(queryTable, sizeof(queryTable));
6841 
6842     //
6843     // The query table has two entries. One for CopyOffloadMaxDuration and
6844     // the second which is the 'NULL' terminator.
6845     //
6846     // Indicate that there is NO call-back routine.
6847     //
6848     queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
6849 
6850     //
6851     // The value to query.
6852     //
6853     queryTable[0].Name = CLASSP_REG_COPY_OFFLOAD_MAX_TARGET_DURATION;
6854 
6855     //
6856     // Where to put the value, the type of the value, default value and length.
6857     //
6858     queryTable[0].EntryContext = &value;
6859     queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
6860     queryTable[0].DefaultData = &value;
6861     queryTable[0].DefaultLength = sizeof(value);
6862 
6863     //
6864     // Try to get the max target duration.
6865     //
6866     status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
6867                                     RegistryPath,
6868                                     queryTable,
6869                                     NULL,
6870                                     NULL);
6871 
6872     //
6873     // Don't allow the user to set the value to lower than the default (4s) so
6874     // they don't break ODX functionality if they accidentally set it too low.
6875     //
6876     if (NT_SUCCESS(status) &&
6877         value > DEFAULT_MAX_TARGET_DURATION) {
6878         *MaxDuration = value;
6879     } else {
6880         *MaxDuration = DEFAULT_MAX_TARGET_DURATION;
6881     }
6882 
6883     TracePrint((TRACE_LEVEL_VERBOSE,
6884                 TRACE_FLAG_PNP,
6885                 "ClasspGetCopyOffloadMaxDuration (%p): Exiting function with status %x (Max Duration %u seconds).\n",
6886                 DeviceObject,
6887                 status,
6888                 *MaxDuration));
6889 
6890     return status;
6891 }
6892 
6893 
6894 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)6895 _IRQL_requires_min_(PASSIVE_LEVEL)
6896 _IRQL_requires_same_
6897 NTSTATUS
6898 ClasspDeviceCopyOffloadProperty(
6899     _In_ PDEVICE_OBJECT DeviceObject,
6900     _Inout_ PIRP Irp,
6901     _Inout_ PSCSI_REQUEST_BLOCK Srb
6902     )
6903 
6904 /*++
6905 
6906 Routine Description:
6907 
6908     This routine returns the copy offload parameters associated with the device.
6909 
6910     This function must be called at IRQL < DISPATCH_LEVEL.
6911 
6912 Arguments:
6913 
6914     DeviceObject - Supplies the device object associated with this request
6915     Irp - The IRP to be processed
6916     Srb - The SRB associated with the request
6917 
6918 Return Value:
6919 
6920     NTSTATUS code
6921 
6922 --*/
6923 
6924 {
6925     NTSTATUS status;
6926     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
6927     PSTORAGE_PROPERTY_QUERY query;
6928     PIO_STACK_LOCATION irpStack;
6929     ULONG length;
6930     ULONG information;
6931     PDEVICE_COPY_OFFLOAD_DESCRIPTOR copyOffloadDescr = (PDEVICE_COPY_OFFLOAD_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
6932 
6933     UNREFERENCED_PARAMETER(Srb);
6934 
6935     PAGED_CODE();
6936 
6937     fdoExtension = DeviceObject->DeviceExtension;
6938     query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
6939     irpStack = IoGetCurrentIrpStackLocation(Irp);
6940     length = 0;
6941     information = 0;
6942 
6943     TracePrint((TRACE_LEVEL_VERBOSE,
6944                 TRACE_FLAG_IOCTL,
6945                 "ClasspDeviceCopyOffloadProperty (%p): Entering function.\n",
6946                 DeviceObject));
6947 
6948     //
6949     // Check proper query type.
6950     //
6951     if (query->QueryType == PropertyExistsQuery) {
6952 
6953         //
6954         // In order to maintain consistency with the how the rest of the properties
6955         // are handled, we shall always return success for PropertyExistsQuery.
6956         //
6957         status = STATUS_SUCCESS;
6958         goto __ClasspDeviceCopyOffloadProperty_Exit;
6959 
6960     } else if (query->QueryType != PropertyStandardQuery) {
6961 
6962         TracePrint((TRACE_LEVEL_ERROR,
6963                     TRACE_FLAG_IOCTL,
6964                     "ClasspDeviceCopyOffloadProperty (%p): Unsupported query type %x for Copy Offload property.\n",
6965                     DeviceObject,
6966                     query->QueryType));
6967 
6968         status = STATUS_NOT_SUPPORTED;
6969         goto __ClasspDeviceCopyOffloadProperty_Exit;
6970     }
6971 
6972     //
6973     // Request validation.
6974     // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
6975     //
6976 
6977     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
6978 
6979         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
6980 
6981         TracePrint((TRACE_LEVEL_ERROR,
6982                     TRACE_FLAG_IOCTL,
6983                     "ClasspDeviceCopyOffloadProperty (%p): Query property for Copy Offload called at incorrect IRQL.\n",
6984                     DeviceObject));
6985 
6986         status = STATUS_INVALID_LEVEL;
6987         goto __ClasspDeviceCopyOffloadProperty_Exit;
6988     }
6989 
6990     length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
6991 
6992     if (length < sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR)) {
6993 
6994         if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
6995 
6996             TracePrint((TRACE_LEVEL_WARNING,
6997                         TRACE_FLAG_IOCTL,
6998                         "ClasspDeviceCopyOffloadProperty (%p): Length %u specified for Copy Offload property enough only for header.\n",
6999                         DeviceObject,
7000                         length));
7001 
7002             information = sizeof(STORAGE_DESCRIPTOR_HEADER);
7003             copyOffloadDescr->Version = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
7004             copyOffloadDescr->Size = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
7005 
7006             status = STATUS_SUCCESS;
7007             goto __ClasspDeviceCopyOffloadProperty_Exit;
7008         }
7009 
7010         TracePrint((TRACE_LEVEL_ERROR,
7011                     TRACE_FLAG_IOCTL,
7012                     "ClasspDeviceCopyOffloadProperty (%p): Incorrect length %u specified for Copy Offload property.\n",
7013                     DeviceObject,
7014                     length));
7015 
7016         status = STATUS_BUFFER_TOO_SMALL;
7017         goto __ClasspDeviceCopyOffloadProperty_Exit;
7018     }
7019 
7020     if (!fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
7021 
7022         TracePrint((TRACE_LEVEL_ERROR,
7023                     TRACE_FLAG_IOCTL,
7024                     "ClasspDeviceCopyOffloadProperty (%p): Command not supported on this device.\n",
7025                     DeviceObject));
7026 
7027         status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
7028         goto __ClasspDeviceCopyOffloadProperty_Exit;
7029     }
7030 
7031     if (!NT_SUCCESS(fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus)) {
7032 
7033         status = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus;
7034 
7035         TracePrint((TRACE_LEVEL_ERROR,
7036                     TRACE_FLAG_IOCTL,
7037                     "ClasspDeviceCopyOffloadProperty (%p): VPD retrieval had failed with %x.\n",
7038                     DeviceObject,
7039                     status));
7040 
7041         goto __ClasspDeviceCopyOffloadProperty_Exit;
7042     }
7043 
7044     //
7045     // Fill in the output buffer.  All data is copied from the FDO extension where we
7046     // cached Block Limits and Block Device Token Limits info when the device was first initialized.
7047     //
7048     RtlZeroMemory(copyOffloadDescr, length);
7049     copyOffloadDescr->Version = 1;
7050     copyOffloadDescr->Size = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
7051     copyOffloadDescr->MaximumTokenLifetime = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer;
7052     copyOffloadDescr->DefaultTokenLifetime = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer;
7053     copyOffloadDescr->MaximumTransferSize = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize;
7054     copyOffloadDescr->OptimalTransferCount = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount;
7055     copyOffloadDescr->MaximumDataDescriptors = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors;
7056 
7057     if (NT_SUCCESS(fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus)) {
7058 
7059         copyOffloadDescr->MaximumTransferLengthPerDescriptor = fdoExtension->FunctionSupportInfo->BlockLimitsData.MaximumTransferLength;
7060         copyOffloadDescr->OptimalTransferLengthPerDescriptor = fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLength;
7061         copyOffloadDescr->OptimalTransferLengthGranularity = fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLengthGranularity;
7062     }
7063 
7064     information = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
7065     status = STATUS_SUCCESS;
7066 
7067 __ClasspDeviceCopyOffloadProperty_Exit:
7068 
7069     //
7070     // Set the size and status in IRP
7071     //
7072     Irp->IoStatus.Information = information;
7073     Irp->IoStatus.Status = status;
7074 
7075     ClassReleaseRemoveLock(DeviceObject, Irp);
7076     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7077 
7078     TracePrint((TRACE_LEVEL_VERBOSE,
7079                 TRACE_FLAG_IOCTL,
7080                 "ClasspDeviceCopyOffloadProperty (%p): Exiting function with status %x.\n",
7081                 DeviceObject,
7082                 status));
7083 
7084     return status;
7085 }
7086 
7087 
7088 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)7089 _IRQL_requires_min_(PASSIVE_LEVEL)
7090 _IRQL_requires_same_
7091 NTSTATUS
7092 ClasspValidateOffloadSupported(
7093     _In_ PDEVICE_OBJECT DeviceObject,
7094     _In_ PIRP Irp
7095     )
7096 
7097 /*++
7098 
7099 Routine Description:
7100 
7101     This routine validates if this device supports offload requests.
7102 
7103     This function must be called at IRQL < DISPATCH_LEVEL.
7104 
7105 Arguments:
7106 
7107     DeviceObject - Supplies the device object associated with this request
7108     Irp - The IRP to be processed
7109 
7110 Return Value:
7111 
7112     NTSTATUS code
7113 
7114 --*/
7115 
7116 {
7117     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
7118     NTSTATUS status;
7119 
7120     PAGED_CODE();
7121 
7122     TracePrint((TRACE_LEVEL_VERBOSE,
7123                 TRACE_FLAG_IOCTL,
7124                 "ClasspValidateOffloadSupported (%p): Entering function. Irp %p\n",
7125                 DeviceObject,
7126                 Irp));
7127 
7128     fdoExt = DeviceObject->DeviceExtension;
7129     status = STATUS_SUCCESS;
7130 
7131     //
7132     // For now this command is only supported by disk devices
7133     //
7134     if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
7135         (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
7136 
7137         if (!fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
7138 
7139             TracePrint((TRACE_LEVEL_ERROR,
7140                         TRACE_FLAG_IOCTL,
7141                         "ClasspValidateOffloadSupported (%p): Command not supported on this disk device.\n",
7142                         DeviceObject));
7143 
7144             status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
7145             goto __ClasspValidateOffloadSupported_Exit;
7146         }
7147 
7148         if (!NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus)) {
7149 
7150             status = fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus;
7151 
7152             TracePrint((TRACE_LEVEL_ERROR,
7153                         TRACE_FLAG_IOCTL,
7154                         "ClasspValidateOffloadSupported (%p): VPD retrieval failed with %x.\n",
7155                         DeviceObject,
7156                         status));
7157 
7158             goto __ClasspValidateOffloadSupported_Exit;
7159         }
7160     } else {
7161 
7162         TracePrint((TRACE_LEVEL_WARNING,
7163                     TRACE_FLAG_IOCTL,
7164                     "ClasspValidateOffloadSupported (%p): Suported only on Disk devices.\n",
7165                     DeviceObject));
7166 
7167         status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
7168         goto __ClasspValidateOffloadSupported_Exit;
7169     }
7170 
7171 __ClasspValidateOffloadSupported_Exit:
7172     TracePrint((TRACE_LEVEL_VERBOSE,
7173                 TRACE_FLAG_IOCTL,
7174                 "ClasspValidateOffloadSupported (%p): Exiting function Irp %p with status %x.\n",
7175                 DeviceObject,
7176                 Irp,
7177                 status));
7178 
7179     return status;
7180 }
7181 
7182 
7183 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)7184 _IRQL_requires_min_(PASSIVE_LEVEL)
7185 _IRQL_requires_same_
7186 NTSTATUS
7187 ClasspValidateOffloadInputParameters(
7188     _In_ PDEVICE_OBJECT DeviceObject,
7189     _In_ PIRP Irp
7190     )
7191 
7192 /*++
7193 
7194 Routine Description:
7195 
7196     This routine does some basic validation of the input parameters of the offload request.
7197 
7198     This function must be called at IRQL < DISPATCH_LEVEL.
7199 
7200 Arguments:
7201 
7202     DeviceObject - Supplies the device object associated with this request
7203     Irp - The IRP to be processed
7204 
7205 Return Value:
7206 
7207     NTSTATUS code
7208 
7209 --*/
7210 
7211 {
7212     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7213     PIO_STACK_LOCATION irpStack;
7214     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
7215     PDEVICE_DATA_SET_RANGE dataSetRanges;
7216     ULONG dataSetRangesCount;
7217     ULONG i;
7218     NTSTATUS status;
7219 
7220     PAGED_CODE();
7221 
7222     TracePrint((TRACE_LEVEL_VERBOSE,
7223                 TRACE_FLAG_IOCTL,
7224                 "ClasspValidateOffloadInputParameters (%p): Entering function Irp %p.\n",
7225                 DeviceObject,
7226                 Irp));
7227 
7228     fdoExtension = DeviceObject->DeviceExtension;
7229     irpStack = IoGetCurrentIrpStackLocation (Irp);
7230     dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
7231     status = STATUS_SUCCESS;
7232 
7233     if (!dsmAttributes) {
7234 
7235         TracePrint((TRACE_LEVEL_ERROR,
7236                     TRACE_FLAG_IOCTL,
7237                     "ClasspValidateOffloadInputParameters (%p): NULL DsmAttributes passed in.\n",
7238                     DeviceObject));
7239 
7240         status = STATUS_INVALID_PARAMETER;
7241         goto __ClasspValidateOffloadInputParameters_Exit;
7242     }
7243 
7244     if ((irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) ||
7245         (irpStack->Parameters.DeviceIoControl.InputBufferLength <
7246         (sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES) + dsmAttributes->ParameterBlockLength + dsmAttributes->DataSetRangesLength))) {
7247 
7248         TracePrint((TRACE_LEVEL_ERROR,
7249                     TRACE_FLAG_IOCTL,
7250                     "ClasspValidateOffloadInputParameters (%p): Input buffer size (%u) too small.\n",
7251                     DeviceObject,
7252                     irpStack->Parameters.DeviceIoControl.InputBufferLength));
7253 
7254         status = STATUS_INVALID_PARAMETER;
7255         goto __ClasspValidateOffloadInputParameters_Exit;
7256     }
7257 
7258     if ((dsmAttributes->DataSetRangesOffset == 0) ||
7259         (dsmAttributes->DataSetRangesLength == 0)) {
7260 
7261         TracePrint((TRACE_LEVEL_ERROR,
7262                     TRACE_FLAG_IOCTL,
7263                     "ClasspValidateOffloadInputParameters (%p): Incorrect DataSetRanges [offset %u, length %u].\n",
7264                     DeviceObject,
7265                     dsmAttributes->DataSetRangesOffset,
7266                     dsmAttributes->DataSetRangesLength));
7267 
7268         status = STATUS_INVALID_PARAMETER;
7269         goto __ClasspValidateOffloadInputParameters_Exit;
7270     }
7271 
7272     dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
7273     dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
7274 
7275     if (dataSetRangesCount == 0) {
7276 
7277         TracePrint((TRACE_LEVEL_ERROR,
7278                     TRACE_FLAG_IOCTL,
7279                     "ClasspValidateOffloadInputParameters (%p): DataSetRanges specifies no extents.\n",
7280                     DeviceObject));
7281 
7282         status = STATUS_INVALID_PARAMETER;
7283         goto __ClasspValidateOffloadInputParameters_Exit;
7284     }
7285 
7286     //
7287     // Some third party disk class drivers do not query the geometry at initialization time,
7288     // so this information may not be available at this time. If that is the case, we'll
7289     // first query that information before proceeding with the rest of our validations.
7290     //
7291     if (fdoExtension->DiskGeometry.BytesPerSector == 0) {
7292         status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
7293         if ((!NT_SUCCESS(status)) || (fdoExtension->DiskGeometry.BytesPerSector == 0)) {
7294             TracePrint((TRACE_LEVEL_ERROR,
7295                         TRACE_FLAG_IOCTL,
7296                         "ClasspValidateOffloadInputParameters (%p): Couldn't retrieve disk geometry, status: %x, bytes/sector: %u.\n",
7297                         DeviceObject,
7298                         status,
7299                         fdoExtension->DiskGeometry.BytesPerSector));
7300 
7301             status = STATUS_INVALID_PARAMETER;
7302             goto __ClasspValidateOffloadInputParameters_Exit;
7303         }
7304     }
7305 
7306     //
7307     // Data must be aligned to sector boundary and
7308     // LengthInBytes must be > 0 for it to be a valid LBA entry
7309     //
7310     for (i = 0; i < dataSetRangesCount; i++) {
7311         if ((dataSetRanges[i].StartingOffset % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
7312             (dataSetRanges[i].LengthInBytes % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
7313             (dataSetRanges[i].LengthInBytes == 0) ) {
7314             TracePrint((TRACE_LEVEL_ERROR,
7315                         TRACE_FLAG_IOCTL,
7316                         "ClasspValidateOffloadInputParameters (%p): Incorrect DataSetRanges entry %u [offset %I64x, length %I64x].\n",
7317                         DeviceObject,
7318                         i,
7319                         dataSetRanges[i].StartingOffset,
7320                         dataSetRanges[i].LengthInBytes));
7321 
7322             status = STATUS_INVALID_PARAMETER;
7323             goto __ClasspValidateOffloadInputParameters_Exit;
7324         }
7325 
7326         if ((ULONGLONG)dataSetRanges[i].StartingOffset + dataSetRanges[i].LengthInBytes > (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart) {
7327 
7328             TracePrint((TRACE_LEVEL_ERROR,
7329                         TRACE_FLAG_IOCTL,
7330                         "ClasspValidateOffloadInputParameters (%p): Error! DataSetRange %u (starting LBA %I64x) specified length %I64x exceeds the medium's capacity (%I64x).\n",
7331                         DeviceObject,
7332                         i,
7333                         dataSetRanges[i].StartingOffset,
7334                         dataSetRanges[i].LengthInBytes,
7335                         fdoExtension->CommonExtension.PartitionLength.QuadPart));
7336 
7337             status = STATUS_NONEXISTENT_SECTOR;
7338             goto __ClasspValidateOffloadInputParameters_Exit;
7339         }
7340     }
7341 
7342 __ClasspValidateOffloadInputParameters_Exit:
7343     TracePrint((TRACE_LEVEL_VERBOSE,
7344                 TRACE_FLAG_IOCTL,
7345                 "ClasspValidateOffloadInputParameters (%p): Exiting function Irp %p with status %x.\n",
7346                 DeviceObject,
7347                 Irp,
7348                 status));
7349 
7350     return status;
7351 }
7352 
7353 
7354 _IRQL_requires_same_
7355 NTSTATUS
ClasspGetTokenOperationCommandBufferLength(_In_ PDEVICE_OBJECT Fdo,_In_ ULONG ServiceAction,_Inout_ PULONG CommandBufferLength,_Out_opt_ PULONG TokenOperationBufferLength,_Out_opt_ PULONG ReceiveTokenInformationBufferLength)7356 ClasspGetTokenOperationCommandBufferLength(
7357     _In_ PDEVICE_OBJECT Fdo,
7358     _In_ ULONG ServiceAction,
7359     _Inout_ PULONG CommandBufferLength,
7360     _Out_opt_ PULONG TokenOperationBufferLength,
7361     _Out_opt_ PULONG ReceiveTokenInformationBufferLength
7362     )
7363 
7364 /*++
7365 
7366 Routine description:
7367 
7368     This routine calculates the buffer length required to service a TokenOperation and its
7369     corresponding ReceiveTokenInformation command.
7370 
7371 Arguments:
7372 
7373     Fdo - The functional device object processing the PopulateToken/WriteUsingToken request
7374     ServiceAction - Used to distinguish between a PopulateToken and a WriteUsingToken operation
7375     CommandBufferLength - Returns the length of the buffer needed to service the token request (i.e. TokenOperation and its corresponding ReceiveTokenInformation command)
7376     TokenOperationBufferLength - Optional parameter, which returns the length of the buffer needed to service just the TokenOperation command.
7377     ReceiveTokenInformationBufferLength - Optional parameter, which returns the length of the buffer needed to service just the ReceiveTokenInformation command.
7378 
7379 Return Value:
7380 
7381     STATUS_SUCCESS
7382 
7383 --*/
7384 
7385 {
7386     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
7387     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
7388     ULONG tokenOperationBufferLength;
7389     ULONG receiveTokenInformationBufferLength;
7390     PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
7391     PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
7392     ULONG hwMaxXferLen;
7393     ULONG bufferLength = 0;
7394     ULONG tokenOperationHeaderSize;
7395     ULONG responseSize;
7396 
7397     TracePrint((TRACE_LEVEL_VERBOSE,
7398                 TRACE_FLAG_IOCTL,
7399                 "ClasspGetTokenOperationCommandBufferLengths (%p): Entering function.\n",
7400                 Fdo));
7401 
7402     NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
7403               NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
7404 
7405     if (ServiceAction == SERVICE_ACTION_POPULATE_TOKEN) {
7406         tokenOperationHeaderSize = FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
7407         responseSize = FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER, TokenDescriptor) + sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR);
7408     } else {
7409         tokenOperationHeaderSize = FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
7410         responseSize = 0;
7411     }
7412 
7413     //
7414     // The TokenOperation command can specify a parameter length of max 2^16.
7415     // If the device has a max limit on the number of range descriptors that can be specified in
7416     // the TokenOperation command, we are limited to the lesser of these two values.
7417     //
7418     if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors == 0) {
7419 
7420         tokenOperationBufferLength = MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH;
7421 
7422     } else {
7423 
7424         tokenOperationBufferLength = MIN(tokenOperationHeaderSize + fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR),
7425                                          MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
7426     }
7427 
7428 
7429     //
7430     // The ReceiveTokenInformation command can specify a parameter length of max 2 ^ 32
7431     // Also, since the sense data can be of variable size, we'll use MAX_SENSE_BUFFER_SIZE.
7432     //
7433     receiveTokenInformationBufferLength = MIN(FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE + responseSize,
7434                                               MAX_RECEIVE_TOKEN_INFORMATION_PARAMETER_DATA_LENGTH);
7435 
7436     //
7437     // Since we're going to reuse the buffer for both the TokenOperation and the ReceiveTokenInformation
7438     // commands, the buffer length needs to handle both operations.
7439     //
7440     bufferLength = MAX(tokenOperationBufferLength, receiveTokenInformationBufferLength);
7441 
7442     //
7443     // The buffer length needs to be further limited to the adapter's capability though.
7444     //
7445     hwMaxXferLen = MIN(fdoData->HwMaxXferLen, adapterDesc->MaximumTransferLength);
7446     bufferLength = MIN(bufferLength, hwMaxXferLen);
7447 
7448     *CommandBufferLength = bufferLength;
7449 
7450     if (TokenOperationBufferLength) {
7451         *TokenOperationBufferLength = tokenOperationBufferLength;
7452     }
7453 
7454     if (ReceiveTokenInformationBufferLength) {
7455         *ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
7456     }
7457 
7458     TracePrint((TRACE_LEVEL_VERBOSE,
7459                 TRACE_FLAG_IOCTL,
7460                 "ClasspGetTokenOperationCommandBufferLengths (%p): Exiting function with bufferLength %u (tokenOpBufLen %u, recTokenInfoBufLen %u).\n",
7461                 Fdo,
7462                 bufferLength,
7463                 tokenOperationBufferLength,
7464                 receiveTokenInformationBufferLength));
7465 
7466     return STATUS_SUCCESS;
7467 }
7468 
7469 
7470 _IRQL_requires_same_
7471 NTSTATUS
ClasspGetTokenOperationDescriptorLimits(_In_ PDEVICE_OBJECT Fdo,_In_ ULONG ServiceAction,_In_ ULONG MaxParameterBufferLength,_Out_ PULONG MaxBlockDescriptorsCount,_Out_ PULONGLONG MaxBlockDescriptorsLength)7472 ClasspGetTokenOperationDescriptorLimits(
7473     _In_ PDEVICE_OBJECT Fdo,
7474     _In_ ULONG ServiceAction,
7475     _In_ ULONG MaxParameterBufferLength,
7476     _Out_ PULONG MaxBlockDescriptorsCount,
7477     _Out_ PULONGLONG MaxBlockDescriptorsLength
7478     )
7479 
7480 /*++
7481 
7482 Routine description:
7483 
7484     This routine calculates the maximum block descriptors and the maximum token transfer size
7485     that can be accomodated in a single TokenOperation command.
7486 
7487 Arguments:
7488 
7489     Fdo - The functional device object processing the PopulateToken/WriteUsingToken request
7490     ServiceAction - Used to distinguish between a PopulateToken and a WriteUsingToken operation
7491     MaxParameterBufferLength - The length constraint of the entire buffer for the parameter list based on other limitations (e.g. adapter max transfer length)
7492     MaxBlockDescriptorsCount - Returns the maximum number of the block range descriptors that can be passed in a single TokenOperation command.
7493     MaxBlockDescriptorsLength - Returns the maximum cumulative number of blocks across all the descriptors that must not be exceeded in a single TokenOperation command.
7494 
7495 Return Value:
7496 
7497     STATUS_SUCCESS
7498 
7499 --*/
7500 
7501 {
7502     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
7503     ULONG tokenOperationHeaderSize = (ServiceAction == SERVICE_ACTION_POPULATE_TOKEN) ?
7504                                      FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor) :
7505                                      FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
7506 
7507     TracePrint((TRACE_LEVEL_VERBOSE,
7508                 TRACE_FLAG_IOCTL,
7509                 "ClasspGetTokenOperationDescriptorLimits (%p): Entering function.\n",
7510                 Fdo));
7511 
7512     NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
7513               NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
7514 
7515     *MaxBlockDescriptorsCount = (MaxParameterBufferLength - tokenOperationHeaderSize) / sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR);
7516     *MaxBlockDescriptorsLength = (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize == 0) ?
7517                                   MAX_TOKEN_TRANSFER_SIZE : fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize;
7518 
7519     TracePrint((TRACE_LEVEL_VERBOSE,
7520                 TRACE_FLAG_IOCTL,
7521                 "ClasspGetTokenOperationDescriptorLimits (%p): Exiting function with MaxDescr %u, MaxXferBlocks %I64u.\n",
7522                 Fdo,
7523                 *MaxBlockDescriptorsCount,
7524                 *MaxBlockDescriptorsLength));
7525 
7526     return STATUS_SUCCESS;
7527 }
7528 
7529 
7530 
7531 _IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)7532 _IRQL_requires_min_(PASSIVE_LEVEL)
7533 _IRQL_requires_same_
7534 VOID
7535 ClasspConvertDataSetRangeToBlockDescr(
7536     _In_    PDEVICE_OBJECT Fdo,
7537     _In_    PVOID BlockDescr,
7538     _Inout_ PULONG CurrentBlockDescrIndex,
7539     _In_    ULONG MaxBlockDescrCount,
7540     _Inout_ PULONG CurrentLbaCount,
7541     _In_    ULONGLONG MaxLbaCount,
7542     _Inout_ PDEVICE_DATA_SET_RANGE DataSetRange,
7543     _Inout_ PULONGLONG TotalSectorsProcessed
7544     )
7545 
7546 /*++
7547 
7548 Routine Description:
7549 
7550     Convert DEVICE_DATA_SET_RANGE entry to be WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR entries.
7551 
7552     As LengthInBytes field in DEVICE_DATA_SET_RANGE structure is 64 bits (bytes)
7553     and LbaCount field in WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR structure is 32 bits (sectors),
7554     it's possible that one DEVICE_DATA_SET_RANGE entry needs multiple
7555     WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR entries. This routine handles the need for that
7556     potential split.
7557 
7558 Arguments:
7559 
7560     Fdo - The functional device object
7561     BlockDescr - Pointer to the start of the Token Operation command's block descriptor
7562     CurrentBlockDescrIndex - Index into the block descriptor at which to update the DataSetRange info
7563                              It also gets updated to return the index to the next empty one.
7564     MaxBlockDescrCount - Maximum number of block descriptors that the device can handle in a single TokenOperation command
7565     CurrentLbaCount - Returns the LBA of the last successfully processed DataSetRange
7566     MaxLbaCount - Maximum transfer size that the device is capable of handling in a single TokenOperation command
7567     DataSetRange - Contains information about one range extent that needs to be converted into a block descriptor
7568     TotalSectorsProcessed - Returns the number of sectors corresponding to the DataSetRange that were succesfully mapped into block descriptors
7569 
7570 Return Value:
7571 
7572     Nothing.
7573 
7574     NOTE: if LengthInBytes does not reach to 0, the conversion for DEVICE_DATA_SET_RANGE entry
7575           is not completed. Further conversion is needed by calling this function again.
7576 
7577 --*/
7578 
7579 {
7580     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7581     PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescr;
7582     ULONGLONG startingSector;
7583     ULONGLONG sectorCount;
7584     ULONGLONG totalSectorCount;
7585     ULONGLONG numberOfOptimalChunks;
7586     USHORT optimalLbaPerDescrGranularity;
7587     ULONG optimalLbaPerDescr;
7588     ULONG maxLbaPerDescr;
7589 
7590     TracePrint((TRACE_LEVEL_VERBOSE,
7591                 TRACE_FLAG_IOCTL,
7592                 "ClasspConvertDataSetRangeToBlockDescr (%p): Entering function. Starting offset %I64x.\n",
7593                 Fdo,
7594                 DataSetRange->StartingOffset));
7595 
7596     fdoExtension = Fdo->DeviceExtension;
7597     blockDescr = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)BlockDescr;
7598     totalSectorCount = 0;
7599 
7600 
7601     //
7602     // Since the OptimalTransferLength and the MaximumTransferLength are overloaded parameters for
7603     // offloaded data transfers and regular read/write requests, it is not safe to use these values
7604     // as they may report back what is used by regular read/write, which will cause a perf degradation
7605     // in the offloaded case, since we may end up limiting the per block range descriptor length
7606     // specified as opposed to what the target can actually handle in a single request.
7607     // So until the SPC spec introduces these values specific to offloaded data transfers, we shall
7608     // ignore them completely. The expectation we have from the target is as follows:
7609     // 1. If the length specified in any of the block range descriptors is greater than the OTL that
7610     //    applies to ODX, the target will internally split into additional descriptors.
7611     // 2. If the above causes it to run out of descriptors, or if the length specified in any of the
7612     //    descriptors is greater than the MTL that applies to ODX, the target will operate on as much
7613     //    data as possible and truncate the request to that point.
7614     //
7615     optimalLbaPerDescrGranularity = 0;
7616     optimalLbaPerDescr = 0;
7617     maxLbaPerDescr = 0;
7618 
7619     if (optimalLbaPerDescr && maxLbaPerDescr) {
7620 
7621         NT_ASSERT(optimalLbaPerDescr <= maxLbaPerDescr);
7622     }
7623 
7624     while ((DataSetRange->LengthInBytes > 0) &&
7625            (*CurrentBlockDescrIndex < MaxBlockDescrCount) &&
7626            (*CurrentLbaCount < MaxLbaCount)) {
7627 
7628         startingSector = (ULONGLONG)(DataSetRange->StartingOffset / fdoExtension->DiskGeometry.BytesPerSector);
7629 
7630         //
7631         // Since the block descriptor has only 4 bytes for the number of logical blocks, we are
7632         // constrained by that theoretical maximum.
7633         //
7634         sectorCount = MIN(DataSetRange->LengthInBytes / fdoExtension->DiskGeometry.BytesPerSector,
7635                           MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR);
7636 
7637         //
7638         // We are constrained by MaxLbaCount.
7639         //
7640         if (((ULONGLONG)*CurrentLbaCount + sectorCount) >= MaxLbaCount) {
7641 
7642             sectorCount = MaxLbaCount - *CurrentLbaCount;
7643         }
7644 
7645         //
7646         // For each descriptor, the block count should be lesser than the MaximumTransferSize
7647         //
7648         if (maxLbaPerDescr > 0) {
7649 
7650             //
7651             // Each block device range descriptor can specify a max number of LBAs
7652             //
7653             sectorCount = MIN(sectorCount, maxLbaPerDescr);
7654         }
7655 
7656         //
7657         // If the number of LBAs specified in the descriptor is greater than the OptimalTransferLength,
7658         // processing of this descriptor by the target may incur a significant delay.
7659         // So in order to allow the target to perform optimally, we'll further limit the number
7660         // of blocks specified in any descriptor to be maximum OptimalTranferLength.
7661         //
7662         if (optimalLbaPerDescr > 0) {
7663 
7664             sectorCount = MIN(sectorCount, optimalLbaPerDescr);
7665         }
7666 
7667         //
7668         // In addition, it should either be an exact multiple of the OptimalTransferLengthGranularity,
7669         // or be lesser than the OptimalTransferLengthGranularity (taken care of here).
7670         //
7671         if (optimalLbaPerDescrGranularity > 0) {
7672 
7673             numberOfOptimalChunks = sectorCount / optimalLbaPerDescrGranularity;
7674 
7675             if (numberOfOptimalChunks > 0) {
7676                 sectorCount = numberOfOptimalChunks * optimalLbaPerDescrGranularity;
7677             }
7678         }
7679 
7680         NT_ASSERT(sectorCount <= MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR);
7681 
7682         REVERSE_BYTES_QUAD(blockDescr[*CurrentBlockDescrIndex].LogicalBlockAddress, &startingSector);
7683         REVERSE_BYTES(blockDescr[*CurrentBlockDescrIndex].TransferLength, &sectorCount);
7684 
7685         totalSectorCount += sectorCount;
7686 
7687         DataSetRange->StartingOffset += sectorCount * fdoExtension->DiskGeometry.BytesPerSector;
7688         DataSetRange->LengthInBytes -= sectorCount * fdoExtension->DiskGeometry.BytesPerSector;
7689 
7690         *CurrentBlockDescrIndex += 1;
7691         *CurrentLbaCount += (ULONG)sectorCount;
7692 
7693         TracePrint((TRACE_LEVEL_INFORMATION,
7694                     TRACE_FLAG_IOCTL,
7695                     "ClasspConvertDataSetRangeToBlockDescr (%p): Descriptor: %u, starting LBA: %I64x, length: %I64x bytes, media size: %I64x.\n",
7696                     Fdo,
7697                     *CurrentBlockDescrIndex - 1,
7698                     startingSector,
7699                     sectorCount * fdoExtension->DiskGeometry.BytesPerSector,
7700                     (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart));
7701     }
7702 
7703     *TotalSectorsProcessed = totalSectorCount;
7704 
7705     TracePrint((TRACE_LEVEL_VERBOSE,
7706                 TRACE_FLAG_IOCTL,
7707                 "ClasspConvertDataSetRangeToBlockDescr (%p): Exiting function (starting offset %I64x). Total sectors processed %I64u.\n",
7708                 Fdo,
7709                 DataSetRange->StartingOffset,
7710                 totalSectorCount));
7711 
7712     return;
7713 }
7714 
7715 _IRQL_requires_same_
7716 PUCHAR
ClasspBinaryToAscii(_In_reads_ (Length)PUCHAR HexBuffer,_In_ ULONG Length,_Inout_ PULONG UpdateLength)7717 ClasspBinaryToAscii(
7718     _In_reads_(Length) PUCHAR HexBuffer,
7719     _In_ ULONG Length,
7720     _Inout_ PULONG UpdateLength
7721     )
7722 
7723 /*++
7724 
7725 Routine Description:
7726 
7727     This routine will convert HexBuffer into an ascii NULL-terminated string.
7728 
7729     Note: This routine will allocate memory for storing the ascii string. It is
7730           the responsibility of the caller to free this buffer.
7731 
7732 Arguments:
7733 
7734     HexBuffer - Pointer to the binary data.
7735     Length - Length, in bytes, of HexBuffer.
7736     UpdateLength - Storage to place the actual length of the returned string.
7737 
7738 Return Value:
7739 
7740     ASCII string equivalent of the hex buffer, or NULL if an error occurred.
7741 
7742 --*/
7743 
7744 {
7745     static const UCHAR integerTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
7746     ULONG i;
7747     ULONG j;
7748     ULONG actualLength;
7749     PUCHAR buffer = NULL;
7750     UCHAR highWord;
7751     UCHAR lowWord;
7752 
7753     TracePrint((TRACE_LEVEL_VERBOSE,
7754                 TRACE_FLAG_IOCTL,
7755                 "ClasspBinaryToAscii (HexBuff %p): Entering function.\n",
7756                 HexBuffer));
7757 
7758     if (!HexBuffer || Length == 0) {
7759         *UpdateLength = 0;
7760         goto __ClasspBinaryToAscii_Exit;
7761     }
7762 
7763     //
7764     // Each byte converts into 2 chars:
7765     // e.g. 0x05 => '0' '5'
7766     //      0x0C => '0' 'C'
7767     //      0x12 => '1' '2'
7768     // And we need a terminating NULL for the string.
7769     //
7770     actualLength = (Length * 2) + 1;
7771 
7772     //
7773     // Allocate the buffer.
7774     //
7775     buffer = ExAllocatePoolWithTag(NonPagedPoolNx, actualLength, CLASSPNP_POOL_TAG_TOKEN_OPERATION);
7776     if (!buffer) {
7777 
7778         TracePrint((TRACE_LEVEL_ERROR,
7779                     TRACE_FLAG_IOCTL,
7780                     "ClasspBinaryToAscii (HexBuff %p): Failed to allocate buffer for ASCII equivalent.\n",
7781                     HexBuffer));
7782 
7783         *UpdateLength = 0;
7784         goto __ClasspBinaryToAscii_Exit;
7785     }
7786 
7787     RtlZeroMemory(buffer, actualLength);
7788 
7789     for (i = 0, j = 0; i < Length; i++) {
7790 
7791         //
7792         // Split out each nibble from the binary byte.
7793         //
7794         highWord = HexBuffer[i] >> 4;
7795         lowWord = HexBuffer[i] & 0x0F;
7796 
7797         //
7798         // Using the lookup table, convert and stuff into
7799         // the ascii buffer.
7800         //
7801         buffer[j++] = integerTable[highWord];
7802 #ifdef _MSC_VER
7803 #pragma warning(suppress: 6386) // PREFast bug means it doesn't see that Length < actualLength
7804 #endif
7805         buffer[j++] = integerTable[lowWord];
7806     }
7807 
7808     //
7809     // Update the caller's length field.
7810     //
7811     *UpdateLength = actualLength;
7812 
7813 __ClasspBinaryToAscii_Exit:
7814 
7815     TracePrint((TRACE_LEVEL_VERBOSE,
7816                 TRACE_FLAG_IOCTL,
7817                 "ClasspBinaryToAscii (HexBuff %p): Exiting function with buffer %s.\n",
7818                 HexBuffer,
7819                 (buffer == NULL) ? "" : (const char*)buffer));
7820 
7821     return buffer;
7822 }
7823 
7824 _IRQL_requires_same_
7825 NTSTATUS
ClasspStorageEventNotification(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)7826 ClasspStorageEventNotification(
7827     _In_ PDEVICE_OBJECT DeviceObject,
7828     _In_ PIRP Irp
7829     )
7830 
7831 /*++
7832 
7833 Routine Description:
7834 
7835     This routine handles an asynchronous event notification (most likely from
7836     port drivers). Currently, we only care about media status change events.
7837 
7838 Arguments:
7839 
7840     DeviceObject - Supplies the device object associated with this request
7841     Irp - The IRP to be processed
7842 
7843 Return Value:
7844 
7845     NTSTATUS code
7846 
7847 --*/
7848 
7849 {
7850     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7851     PIO_STACK_LOCATION irpStack;
7852     PSTORAGE_EVENT_NOTIFICATION storageEvents;
7853     NTSTATUS status;
7854 
7855     TracePrint((TRACE_LEVEL_VERBOSE,
7856                 TRACE_FLAG_IOCTL,
7857                 "ClasspStorageEventNotification (%p): Entering function Irp %p.\n",
7858                 DeviceObject,
7859                 Irp));
7860 
7861     fdoExtension = DeviceObject->DeviceExtension;
7862     irpStack = IoGetCurrentIrpStackLocation (Irp);
7863     storageEvents = Irp->AssociatedIrp.SystemBuffer;
7864     status = STATUS_SUCCESS;
7865 
7866     if (!storageEvents) {
7867 
7868         TracePrint((TRACE_LEVEL_ERROR,
7869                     TRACE_FLAG_IOCTL,
7870                     "ClasspStorageEventNotification (%p): NULL storage events passed in.\n",
7871                     DeviceObject));
7872 
7873         status = STATUS_INVALID_PARAMETER;
7874         goto __ClasspStorageEventNotification_Exit;
7875     }
7876 
7877     if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_EVENT_NOTIFICATION)) {
7878 
7879         TracePrint((TRACE_LEVEL_ERROR,
7880                     TRACE_FLAG_IOCTL,
7881                     "ClasspStorageEventNotification (%p): Input buffer size (%u) too small.\n",
7882                     DeviceObject,
7883                     irpStack->Parameters.DeviceIoControl.InputBufferLength));
7884 
7885         status = STATUS_INFO_LENGTH_MISMATCH;
7886         goto __ClasspStorageEventNotification_Exit;
7887     }
7888 
7889     if ((storageEvents->Version != STORAGE_EVENT_NOTIFICATION_VERSION_V1) ||
7890         (storageEvents->Size != sizeof(STORAGE_EVENT_NOTIFICATION))) {
7891 
7892         TracePrint((TRACE_LEVEL_ERROR,
7893                     TRACE_FLAG_IOCTL,
7894                     "ClasspStorageEventNotification (%p): Invalid version/size [version %u, size %u].\n",
7895                     DeviceObject,
7896                     storageEvents->Version,
7897                     storageEvents->Size));
7898 
7899         status = STATUS_INVALID_PARAMETER;
7900         goto __ClasspStorageEventNotification_Exit;
7901     }
7902 
7903     //
7904     // Handle a media status event.
7905     //
7906     if (storageEvents->Events & STORAGE_EVENT_MEDIA_STATUS) {
7907 
7908         //
7909         // Only initiate operation if underlying port driver supports asynchronous notification
7910         // and this is the FDO.
7911         //
7912         if ((fdoExtension->CommonExtension.IsFdo == TRUE) &&
7913             (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported)) {
7914             ClassCheckMediaState(fdoExtension);
7915         } else {
7916             status = STATUS_NOT_SUPPORTED;
7917         }
7918 
7919     }
7920 
7921 __ClasspStorageEventNotification_Exit:
7922     TracePrint((TRACE_LEVEL_VERBOSE,
7923                 TRACE_FLAG_IOCTL,
7924                 "ClasspStorageEventNotification (%p): Exiting function Irp %p with status %x.\n",
7925                 DeviceObject,
7926                 Irp,
7927                 status));
7928 
7929     Irp->IoStatus.Information = 0;
7930     Irp->IoStatus.Status = status;
7931     ClassReleaseRemoveLock(DeviceObject, Irp);
7932     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7933 
7934     return status;
7935 }
7936 
7937 VOID
ClasspZeroQERR(_In_ PDEVICE_OBJECT DeviceObject)7938 ClasspZeroQERR(
7939     _In_ PDEVICE_OBJECT DeviceObject
7940     )
7941 /*++
7942 
7943 Routine Description:
7944 
7945     This routine will attempt to set the QERR bit of the mode Control page to
7946     zero.
7947 
7948 Arguments:
7949 
7950     DeviceObject - Supplies the device object associated with this request
7951 
7952 Return Value:
7953 
7954     None
7955 
7956 --*/
7957 {
7958     PMODE_PARAMETER_HEADER modeData = NULL;
7959     PMODE_CONTROL_PAGE pageData = NULL;
7960     ULONG size = 0;
7961 
7962     modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
7963                                     MODE_PAGE_DATA_SIZE,
7964                                     CLASS_TAG_MODE_DATA);
7965 
7966     if (modeData == NULL) {
7967         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_SCSI, "ClasspZeroQERR: Unable to allocate mode data buffer\n"));
7968         goto ClasspZeroQERR_Exit;
7969     }
7970 
7971     RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
7972 
7973     size = ClassModeSense(DeviceObject,
7974                             (PCHAR) modeData,
7975                             MODE_PAGE_DATA_SIZE,
7976                             MODE_PAGE_CONTROL);
7977 
7978     if (size < sizeof(MODE_PARAMETER_HEADER)) {
7979 
7980         //
7981         // Retry the request in case of a check condition.
7982         //
7983 
7984         size = ClassModeSense(DeviceObject,
7985                                 (PCHAR) modeData,
7986                                 MODE_PAGE_DATA_SIZE,
7987                                 MODE_PAGE_CONTROL);
7988 
7989         if (size < sizeof(MODE_PARAMETER_HEADER)) {
7990             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_SCSI, "ClasspZeroQERR: Mode Sense failed\n"));
7991             goto ClasspZeroQERR_Exit;
7992         }
7993     }
7994 
7995     //
7996     // If the size is greater than size indicated by the mode data reset
7997     // the data to the mode data.
7998     //
7999 
8000     if (size > (ULONG) (modeData->ModeDataLength + 1)) {
8001         size = modeData->ModeDataLength + 1;
8002     }
8003 
8004     //
8005     // Look for control page in the returned mode page data.
8006     //
8007 
8008     pageData = ClassFindModePage((PCHAR) modeData,
8009                                     size,
8010                                     MODE_PAGE_CONTROL,
8011                                     TRUE);
8012 
8013     if (pageData) {
8014         TracePrint((TRACE_LEVEL_VERBOSE,
8015                     TRACE_FLAG_SCSI,
8016                     "ClasspZeroQERR (%p): Current settings: QERR = %u, TST = %u, TAS = %u.\n",
8017                     DeviceObject,
8018                     pageData->QERR,
8019                     pageData->TST,
8020                     pageData->TAS));
8021 
8022         if (pageData->QERR != 0) {
8023             NTSTATUS status;
8024             UCHAR pageSavable = 0;
8025 
8026             //
8027             // Set QERR to 0 with a Mode Select command.  Re-use the modeData
8028             // and pageData structures.
8029             //
8030             pageData->QERR = 0;
8031 
8032             //
8033             // We use the original Page Savable (PS) value for the Save Pages
8034             // (SP) bit due to behavior described under the MODE SELECT(6)
8035             // section of SPC-4.
8036             //
8037             pageSavable = pageData->PageSavable;
8038 
8039             status = ClasspModeSelect(DeviceObject,
8040                                      (PCHAR)modeData,
8041                                      size,
8042                                      pageSavable);
8043 
8044             if (!NT_SUCCESS(status)) {
8045                 TracePrint((TRACE_LEVEL_WARNING,
8046                     TRACE_FLAG_SCSI,
8047                     "ClasspZeroQERR (%p): Failed to set QERR = 0 with status %x\n",
8048                     DeviceObject,
8049                     status));
8050             }
8051         }
8052     }
8053 
8054 ClasspZeroQERR_Exit:
8055 
8056     if (modeData != NULL) {
8057         ExFreePool(modeData);
8058     }
8059 }
8060 
_IRQL_requires_max_(PASSIVE_LEVEL)8061 _IRQL_requires_max_(PASSIVE_LEVEL)
8062 NTSTATUS
8063 ClasspPowerActivateDevice(
8064     _In_ PDEVICE_OBJECT DeviceObject
8065     )
8066 /*++
8067 
8068 Routine Description:
8069 
8070     This routine synchronously sends an IOCTL_STORAGE_POWER_ACTIVE to the port
8071     PDO in order to take an active reference on the given device.  The device
8072     will remain powered up and active for as long as this active reference is
8073     taken.
8074 
8075     The caller should ensure idle power management is enabled for the device
8076     before calling this function.
8077 
8078     Call ClasspPowerIdleDevice to release the active reference.
8079 
8080 Arguments:
8081 
8082     DeviceObject - Supplies the FDO associated with this request.
8083 
8084 Return Value:
8085 
8086     STATUS_SUCCESS if the active reference was successfully taken.
8087 
8088 --*/
8089 {
8090     NTSTATUS status = STATUS_UNSUCCESSFUL;
8091     PIRP irp;
8092     KEVENT event;
8093     IO_STATUS_BLOCK ioStatus;
8094     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
8095 
8096     NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
8097     NT_ASSERT(fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled);
8098 
8099     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
8100 
8101     irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_POWER_ACTIVE,
8102                                         fdoExtension->LowerPdo,
8103                                         NULL,
8104                                         0,
8105                                         NULL,
8106                                         0,
8107                                         FALSE,
8108                                         &event,
8109                                         &ioStatus);
8110 
8111     if (irp != NULL) {
8112         status = IoCallDriver(fdoExtension->LowerPdo, irp);
8113         if (status == STATUS_PENDING) {
8114             (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
8115             status = ioStatus.Status;
8116         }
8117     } else {
8118         status = STATUS_INSUFFICIENT_RESOURCES;
8119     }
8120 
8121     return status;
8122 }
8123 
_IRQL_requires_max_(PASSIVE_LEVEL)8124 _IRQL_requires_max_(PASSIVE_LEVEL)
8125 NTSTATUS
8126 ClasspPowerIdleDevice(
8127     _In_ PDEVICE_OBJECT DeviceObject
8128     )
8129 /*++
8130 
8131 Routine Description:
8132 
8133     This routine synchronously sends an IOCTL_STORAGE_POWER_IDLE to the port
8134     PDO in order to release an active reference on the given device.
8135 
8136     A call to ClasspPowerActivateDevice *must* have preceded a call to this
8137     function.
8138 
8139     The caller should ensure idle power management is enabled for the device
8140     before calling this function.
8141 
8142 Arguments:
8143 
8144     DeviceObject - Supplies the FDO associated with this request.
8145 
8146 Return Value:
8147 
8148     STATUS_SUCCESS if the active reference was successfully released.
8149 
8150 --*/
8151 {
8152     NTSTATUS status = STATUS_UNSUCCESSFUL;
8153     PIRP irp;
8154     KEVENT event;
8155     IO_STATUS_BLOCK ioStatus;
8156     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
8157 
8158     NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
8159     NT_ASSERT(fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled);
8160 
8161     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
8162 
8163     irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_POWER_IDLE,
8164                                         fdoExtension->LowerPdo,
8165                                         NULL,
8166                                         0,
8167                                         NULL,
8168                                         0,
8169                                         FALSE,
8170                                         &event,
8171                                         &ioStatus);
8172 
8173     if (irp != NULL) {
8174         status = IoCallDriver(fdoExtension->LowerPdo, irp);
8175         if (status == STATUS_PENDING) {
8176             (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
8177             status = ioStatus.Status;
8178         }
8179     } else {
8180         status = STATUS_INSUFFICIENT_RESOURCES;
8181     }
8182 
8183     return status;
8184 }
8185 
8186 
8187 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8188 
8189 NTSTATUS
ClasspGetHwFirmwareInfo(_In_ PDEVICE_OBJECT DeviceObject)8190 ClasspGetHwFirmwareInfo(
8191     _In_ PDEVICE_OBJECT DeviceObject
8192     )
8193 {
8194     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8195     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
8196 
8197     PSTORAGE_HW_FIRMWARE_INFO firmwareInfo = NULL;
8198     PSTORAGE_HW_FIRMWARE_INFO_QUERY query = NULL;
8199 
8200     IO_STATUS_BLOCK ioStatus = {0};
8201     ULONG dataLength = sizeof(STORAGE_HW_FIRMWARE_INFO);
8202     ULONG iteration = 1;
8203 
8204     CLASS_FUNCTION_SUPPORT oldState;
8205     KLOCK_QUEUE_HANDLE  lockHandle;
8206 
8207     //
8208     // Try to get firmware information that contains only one slot.
8209     // We will retry the query if the required buffer size is bigger than that.
8210     //
8211 retry:
8212 
8213     firmwareInfo = ExAllocatePoolWithTag(NonPagedPoolNx, dataLength, CLASSPNP_POOL_TAG_FIRMWARE);
8214 
8215     if (firmwareInfo == NULL) {
8216         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: cannot allocate memory to hold data. \n"));
8217         return STATUS_INSUFFICIENT_RESOURCES;
8218     }
8219 
8220     RtlZeroMemory(firmwareInfo, dataLength);
8221 
8222     //
8223     // Set up query data, making sure the "Flags" field indicating the request is for device itself.
8224     //
8225     query = (PSTORAGE_HW_FIRMWARE_INFO_QUERY)firmwareInfo;
8226 
8227     query->Version = sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY);
8228     query->Size = sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY);
8229     query->Flags = 0;
8230 
8231     //
8232     // On the first pass we just want to get the first few
8233     // bytes of the descriptor so we can read it's size
8234     //
8235     ClassSendDeviceIoControlSynchronous(IOCTL_STORAGE_FIRMWARE_GET_INFO,
8236                                         commonExtension->LowerDeviceObject,
8237                                         query,
8238                                         sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY),
8239                                         dataLength,
8240                                         FALSE,
8241                                         &ioStatus
8242                                         );
8243 
8244     if (!NT_SUCCESS(ioStatus.Status) &&
8245         (ioStatus.Status != STATUS_BUFFER_OVERFLOW)) {
8246         if (ClasspLowerLayerNotSupport(ioStatus.Status)) {
8247             oldState = InterlockedCompareExchange((PLONG)(&fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport), (LONG)NotSupported, (ULONG)SupportUnknown);
8248         }
8249 
8250         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: error %lx trying to "
8251                                                         "query hardware firmware information #%d \n", ioStatus.Status, iteration));
8252         FREE_POOL(firmwareInfo);
8253         return ioStatus.Status;
8254     }
8255 
8256     //
8257     // Catch implementation issues from lower level driver.
8258     //
8259     if ((firmwareInfo->Version < sizeof(STORAGE_HW_FIRMWARE_INFO)) ||
8260         (firmwareInfo->Size < sizeof(STORAGE_HW_FIRMWARE_INFO)) ||
8261         (firmwareInfo->SlotCount == 0) ||
8262         (firmwareInfo->ImagePayloadMaxSize > fdoExtension->AdapterDescriptor->MaximumTransferLength)) {
8263 
8264         oldState = InterlockedCompareExchange((PLONG)(&fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport), (LONG)NotSupported, (ULONG)SupportUnknown);
8265 
8266         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: error in returned data! "
8267                                                         "Version: 0x%X, Size: 0x%X, SlotCount: 0x%X, ActiveSlot: 0x%X, PendingActiveSlot: 0x%X, ImagePayloadMaxSize: 0x%X \n",
8268                                                         firmwareInfo->Version,
8269                                                         firmwareInfo->Size,
8270                                                         firmwareInfo->SlotCount,
8271                                                         firmwareInfo->ActiveSlot,
8272                                                         firmwareInfo->PendingActivateSlot,
8273                                                         firmwareInfo->ImagePayloadMaxSize));
8274 
8275         FREE_POOL(firmwareInfo);
8276         return STATUS_UNSUCCESSFUL;
8277     }
8278 
8279     //
8280     // If the data size is bigger than sizeof(STORAGE_HW_FIRMWARE_INFO), e.g. device has more than one firmware slot,
8281     // allocate a buffer to get all the data.
8282     //
8283     if ((firmwareInfo->Size > sizeof(STORAGE_HW_FIRMWARE_INFO)) &&
8284         (iteration < 2)) {
8285 
8286         dataLength = max(firmwareInfo->Size, sizeof(STORAGE_HW_FIRMWARE_INFO) + sizeof(STORAGE_HW_FIRMWARE_SLOT_INFO) * (firmwareInfo->SlotCount - 1));
8287 
8288         //
8289         // Retry the query with required buffer length.
8290         //
8291         FREE_POOL(firmwareInfo);
8292         iteration++;
8293         goto retry;
8294     }
8295 
8296 
8297     //
8298     // Set the support status and use the memory we've allocated as caching buffer.
8299     // In case of a competing thread already set the state, it will assign the caching buffer so release the current allocated one.
8300     //
8301     KeAcquireInStackQueuedSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
8302 
8303     oldState = InterlockedCompareExchange((PLONG)(&fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport), (LONG)Supported, (ULONG)SupportUnknown);
8304 
8305     if (oldState == SupportUnknown) {
8306         fdoExtension->FunctionSupportInfo->HwFirmwareInfo = firmwareInfo;
8307     } else if (oldState == Supported) {
8308         //
8309         // swap the buffers to keep the latest version.
8310         //
8311         PSTORAGE_HW_FIRMWARE_INFO cachedInfo = fdoExtension->FunctionSupportInfo->HwFirmwareInfo;
8312 
8313         fdoExtension->FunctionSupportInfo->HwFirmwareInfo = firmwareInfo;
8314 
8315         FREE_POOL(cachedInfo);
8316     } else {
8317         FREE_POOL(firmwareInfo);
8318     }
8319 
8320     KeReleaseInStackQueuedSpinLock(&lockHandle);
8321 
8322     return ioStatus.Status;
8323 } // end ClasspGetHwFirmwareInfo()
8324 
8325 #endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8326 
8327 #ifndef __REACTOS__ // the functions is not used
8328 __inline
8329 BOOLEAN
ClassDeviceHwFirmwareIsPortDriverSupported(_In_ PDEVICE_OBJECT DeviceObject)8330 ClassDeviceHwFirmwareIsPortDriverSupported(
8331     _In_  PDEVICE_OBJECT DeviceObject
8332     )
8333 /*
8334 Routine Description:
8335 
8336     This function informs the caller whether the port driver supports hardware firmware requests.
8337 
8338 Arguments:
8339     DeviceObject: The target object.
8340 
8341 Return Value:
8342 
8343     TRUE if the port driver is supported.
8344 
8345 --*/
8346 {
8347     //
8348     // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only.
8349     // Don't process it if we don't have a miniport descriptor.
8350     //
8351     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8352     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
8353 
8354     BOOLEAN isSupported = FALSE;
8355     if (commonExtension->IsFdo && (fdoExtension->MiniportDescriptor != NULL)) {
8356         isSupported = ((fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetStorport) ||
8357                        (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSpaceport) ||
8358                        (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSDport   ));
8359     }
8360 
8361     return isSupported;
8362 }
8363 #endif
8364 
8365 NTSTATUS
ClassDeviceHwFirmwareGetInfoProcess(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp)8366 ClassDeviceHwFirmwareGetInfoProcess(
8367     _In_ PDEVICE_OBJECT DeviceObject,
8368     _Inout_ PIRP Irp
8369     )
8370 /*
8371 Routine Description:
8372 
8373     This function processes the Storage Hardware Firmware Get Information request.
8374     If the information is not cached yet, it gets from lower level driver.
8375 
8376 Arguments:
8377     DeviceObject: The target FDO.
8378     Irp: The IRP which will contain the output buffer upon completion.
8379 
8380 Return Value:
8381 
8382     NTSTATUS code.
8383 
8384 --*/
8385 {
8386     NTSTATUS status = STATUS_SUCCESS;
8387 
8388 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8389 
8390     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8391     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
8392     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
8393     PSTORAGE_HW_FIRMWARE_INFO_QUERY query = (PSTORAGE_HW_FIRMWARE_INFO_QUERY)Irp->AssociatedIrp.SystemBuffer;
8394     BOOLEAN passDown = FALSE;
8395     BOOLEAN copyData = FALSE;
8396 
8397 
8398     //
8399     // Input buffer is not big enough to contain required input information.
8400     //
8401     if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY)) {
8402 
8403         status = STATUS_INFO_LENGTH_MISMATCH;
8404         goto Exit_Firmware_Get_Info;
8405     }
8406 
8407     //
8408     // Output buffer is too small to contain return data.
8409     //
8410     if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_HW_FIRMWARE_INFO)) {
8411 
8412         status = STATUS_BUFFER_TOO_SMALL;
8413         goto Exit_Firmware_Get_Info;
8414     }
8415 
8416     //
8417     // Only process the request for a supported port driver.
8418     //
8419     if (!ClassDeviceHwFirmwareIsPortDriverSupported(DeviceObject)) {
8420         status = STATUS_NOT_IMPLEMENTED;
8421         goto Exit_Firmware_Get_Info;
8422     }
8423 
8424     //
8425     // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation
8426     // of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet.
8427     //
8428     if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) {
8429 
8430         status = STATUS_UNSUCCESSFUL;
8431         goto Exit_Firmware_Get_Info;
8432     }
8433 
8434     //
8435     // Process the situation that request should be forwarded to lower level.
8436     //
8437     if (!commonExtension->IsFdo) {
8438         passDown = TRUE;
8439     }
8440 
8441     if ((query->Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER) != 0) {
8442         passDown = TRUE;
8443     }
8444 
8445     if (passDown) {
8446 
8447         IoCopyCurrentIrpStackLocationToNext(Irp);
8448 
8449         ClassReleaseRemoveLock(DeviceObject, Irp);
8450         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8451         return status;
8452     }
8453 
8454     //
8455     // The request is for a FDO. Process the request.
8456     //
8457     if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
8458         status = STATUS_NOT_IMPLEMENTED;
8459         goto Exit_Firmware_Get_Info;
8460     } else {
8461         //
8462         // Retrieve information from lower layer for the request. The cached information is not used
8463         // in case device firmware information changed.
8464         //
8465         status = ClasspGetHwFirmwareInfo(DeviceObject);
8466         copyData = NT_SUCCESS(status);
8467     }
8468 
8469 Exit_Firmware_Get_Info:
8470 
8471     if (copyData) {
8472         //
8473         // Firmware information is already cached in classpnp. Return a copy.
8474         //
8475         KLOCK_QUEUE_HANDLE lockHandle;
8476         KeAcquireInStackQueuedSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
8477 
8478         ULONG dataLength = min(irpStack->Parameters.DeviceIoControl.OutputBufferLength, fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Size);
8479 
8480         memcpy(Irp->AssociatedIrp.SystemBuffer, fdoExtension->FunctionSupportInfo->HwFirmwareInfo, dataLength);
8481 
8482         KeReleaseInStackQueuedSpinLock(&lockHandle);
8483 
8484         Irp->IoStatus.Information = dataLength;
8485     }
8486 
8487     Irp->IoStatus.Status = status;
8488 
8489 #else
8490     status = STATUS_NOT_IMPLEMENTED;
8491     Irp->IoStatus.Status = status;
8492 #endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8493 
8494     ClassReleaseRemoveLock(DeviceObject, Irp);
8495     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8496 
8497     return status;
8498 }
8499 
8500 
8501 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8502 _IRQL_requires_same_
_IRQL_requires_max_(DISPATCH_LEVEL)8503 _IRQL_requires_max_(DISPATCH_LEVEL)
8504 NTSTATUS
8505 ClassHwFirmwareDownloadComplete (
8506     _In_ PDEVICE_OBJECT Fdo,
8507     _In_ PIRP Irp,
8508     _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
8509     )
8510 {
8511     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
8512 
8513     PIRP originalIrp;
8514 
8515     //
8516     // Free the allocated buffer for firmware image.
8517     //
8518     if (Context != NULL) {
8519         FREE_POOL(Context);
8520     }
8521 
8522     originalIrp = irpStack->Parameters.Others.Argument1;
8523 
8524     NT_ASSERT(originalIrp != NULL);
8525 
8526     originalIrp->IoStatus.Status = Irp->IoStatus.Status;
8527     originalIrp->IoStatus.Information = Irp->IoStatus.Information;
8528 
8529     ClassReleaseRemoveLock(Fdo, originalIrp);
8530     ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
8531 
8532     IoFreeIrp(Irp);
8533 
8534     return STATUS_MORE_PROCESSING_REQUIRED;
8535 
8536 } // end ClassHwFirmwareDownloadComplete()
8537 #endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8538 
8539 
8540 NTSTATUS
ClassDeviceHwFirmwareDownloadProcess(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)8541 ClassDeviceHwFirmwareDownloadProcess(
8542     _In_ PDEVICE_OBJECT DeviceObject,
8543     _Inout_ PIRP Irp,
8544     _Inout_ PSCSI_REQUEST_BLOCK Srb
8545     )
8546 {
8547     NTSTATUS status = STATUS_SUCCESS;
8548 
8549 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8550 
8551     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8552     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
8553 
8554     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
8555     PSTORAGE_HW_FIRMWARE_DOWNLOAD firmwareDownload = (PSTORAGE_HW_FIRMWARE_DOWNLOAD)Irp->AssociatedIrp.SystemBuffer;
8556     BOOLEAN passDown = FALSE;
8557     ULONG   i;
8558     ULONG   bufferSize = 0;
8559     PUCHAR  firmwareImageBuffer = NULL;
8560     PIRP irp2 = NULL;
8561     PIO_STACK_LOCATION newStack = NULL;
8562     PCDB cdb = NULL;
8563     BOOLEAN lockHeld = FALSE;
8564     KLOCK_QUEUE_HANDLE lockHandle;
8565 
8566 
8567     //
8568     // Input buffer is not big enough to contain required input information.
8569     //
8570     if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HW_FIRMWARE_DOWNLOAD)) {
8571 
8572         status = STATUS_INFO_LENGTH_MISMATCH;
8573         goto Exit_Firmware_Download;
8574     }
8575 
8576     //
8577     // Input buffer basic validation.
8578     //
8579     if ((firmwareDownload->Version < sizeof(STORAGE_HW_FIRMWARE_DOWNLOAD)) ||
8580         (firmwareDownload->Size > irpStack->Parameters.DeviceIoControl.InputBufferLength) ||
8581         ((firmwareDownload->BufferSize + FIELD_OFFSET(STORAGE_HW_FIRMWARE_DOWNLOAD, ImageBuffer)) > firmwareDownload->Size)) {
8582 
8583         status = STATUS_INVALID_PARAMETER;
8584         goto Exit_Firmware_Download;
8585     }
8586 
8587     //
8588     // Only process the request for a supported port driver.
8589     //
8590     if (!ClassDeviceHwFirmwareIsPortDriverSupported(DeviceObject)) {
8591         status = STATUS_NOT_IMPLEMENTED;
8592         goto Exit_Firmware_Download;
8593     }
8594 
8595     //
8596     // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation
8597     // of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet.
8598     //
8599     if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) {
8600 
8601         status = STATUS_UNSUCCESSFUL;
8602         goto Exit_Firmware_Download;
8603     }
8604 
8605     //
8606     // Process the situation that request should be forwarded to lower level.
8607     //
8608     if (!commonExtension->IsFdo) {
8609         passDown = TRUE;
8610     }
8611 
8612     if ((firmwareDownload->Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER) != 0) {
8613         passDown = TRUE;
8614     }
8615 
8616     if (passDown) {
8617 
8618         IoCopyCurrentIrpStackLocationToNext(Irp);
8619 
8620         ClassReleaseRemoveLock(DeviceObject, Irp);
8621         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8622         FREE_POOL(Srb);
8623         return status;
8624     }
8625 
8626     //
8627     // If firmware information hasn't been cached in classpnp, retrieve it.
8628     //
8629     if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
8630         if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
8631             status = STATUS_NOT_IMPLEMENTED;
8632             goto Exit_Firmware_Download;
8633         } else {
8634             //
8635             // If this is the first time of retrieving firmware information,
8636             // send request to lower level to get it.
8637             //
8638             status = ClasspGetHwFirmwareInfo(DeviceObject);
8639 
8640             if (!NT_SUCCESS(status)) {
8641                 goto Exit_Firmware_Download;
8642             }
8643         }
8644     }
8645 
8646     //
8647     // Fail the request if the firmware information cannot be retrieved.
8648     //
8649     if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
8650         if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
8651             status = STATUS_NOT_IMPLEMENTED;
8652         } else {
8653             status = STATUS_UNSUCCESSFUL;
8654         }
8655 
8656         goto Exit_Firmware_Download;
8657     }
8658 
8659     //
8660     // Acquire the SyncLock to ensure the HwFirmwareInfo pointer doesn't change
8661     // while we're dereferencing it.
8662     //
8663     lockHeld = TRUE;
8664     KeAcquireInStackQueuedSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
8665 
8666     //
8667     // Validate the device support
8668     //
8669     if ((fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SupportUpgrade == FALSE) ||
8670         (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment == 0)) {
8671         status = STATUS_NOT_SUPPORTED;
8672         goto Exit_Firmware_Download;
8673     }
8674 
8675     //
8676     // Check if the slot can be used to hold firmware image.
8677     //
8678     for (i = 0; i < fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount; i++) {
8679         if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Slot[i].SlotNumber == firmwareDownload->Slot) {
8680             break;
8681         }
8682     }
8683 
8684     if ((i >= fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount) ||
8685         (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Slot[i].ReadOnly == TRUE)) {
8686         //
8687         // Either the slot number is out of scope or the slot is read-only.
8688         //
8689         status = STATUS_INVALID_PARAMETER;
8690         goto Exit_Firmware_Download;
8691     }
8692 
8693     //
8694     // Buffer size and alignment validation.
8695     // Max Offset and Buffer Size can be represented by SCSI command is max value for 3 bytes.
8696     //
8697     if ((firmwareDownload->BufferSize == 0) ||
8698         ((firmwareDownload->BufferSize % fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment) != 0) ||
8699         (firmwareDownload->BufferSize > fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadMaxSize) ||
8700         (firmwareDownload->BufferSize > fdoExtension->AdapterDescriptor->MaximumTransferLength) ||
8701         ((firmwareDownload->Offset % fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment) != 0) ||
8702         (firmwareDownload->Offset > 0xFFFFFF) ||
8703         (firmwareDownload->BufferSize > 0xFFFFFF)) {
8704 
8705         status = STATUS_INVALID_PARAMETER;
8706         goto Exit_Firmware_Download;
8707     }
8708 
8709 
8710     //
8711     // Process the request by translating it into WRITE BUFFER command.
8712     //
8713     if (((ULONG_PTR)firmwareDownload->ImageBuffer % fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment) != 0) {
8714         //
8715         // Allocate buffer aligns to ImagePayloadAlignment to accommodate the alignment requirement.
8716         //
8717         bufferSize = ALIGN_UP_BY(firmwareDownload->BufferSize, fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment);
8718 
8719         //
8720         // We're done accessing HwFirmwareInfo at this point so we can release
8721         // the SyncLock.
8722         //
8723         NT_ASSERT(lockHeld);
8724         KeReleaseInStackQueuedSpinLock(&lockHandle);
8725         lockHeld = FALSE;
8726 
8727 #ifdef _MSC_VER
8728 #pragma prefast(suppress:6014, "The allocated memory that firmwareImageBuffer points to will be freed in ClassHwFirmwareDownloadComplete().")
8729 #endif
8730         firmwareImageBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, CLASSPNP_POOL_TAG_FIRMWARE);
8731 
8732         if (firmwareImageBuffer == NULL) {
8733             status = STATUS_INSUFFICIENT_RESOURCES;
8734             goto Exit_Firmware_Download;
8735         }
8736 
8737         RtlZeroMemory(firmwareImageBuffer, bufferSize);
8738 
8739         RtlCopyMemory(firmwareImageBuffer, firmwareDownload->ImageBuffer, (ULONG)firmwareDownload->BufferSize);
8740 
8741     } else {
8742         NT_ASSERT(lockHeld);
8743         KeReleaseInStackQueuedSpinLock(&lockHandle);
8744         lockHeld = FALSE;
8745 
8746         firmwareImageBuffer = firmwareDownload->ImageBuffer;
8747         bufferSize = (ULONG)firmwareDownload->BufferSize;
8748     }
8749 
8750     //
8751     // Allocate a new irp to send the WRITE BUFFER command down.
8752     // Similar process as IOCTL_STORAGE_CHECK_VERIFY.
8753     //
8754     irp2 = IoAllocateIrp((CCHAR)(DeviceObject->StackSize + 3), FALSE);
8755 
8756     if (irp2 == NULL) {
8757         status = STATUS_INSUFFICIENT_RESOURCES;
8758 
8759         if (firmwareImageBuffer != firmwareDownload->ImageBuffer) {
8760             FREE_POOL(firmwareImageBuffer);
8761         }
8762 
8763         goto Exit_Firmware_Download;
8764     }
8765 
8766     //
8767     // Make sure to acquire the lock for the new irp.
8768     //
8769     ClassAcquireRemoveLock(DeviceObject, irp2);
8770 
8771     irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
8772     IoSetNextIrpStackLocation(irp2);
8773 
8774     //
8775     // Set the top stack location and shove the master Irp into the
8776     // top location
8777     //
8778     newStack = IoGetCurrentIrpStackLocation(irp2);
8779     newStack->Parameters.Others.Argument1 = Irp;
8780     newStack->DeviceObject = DeviceObject;
8781 
8782     //
8783     // Stick the firmware download completion routine onto the stack
8784     // and prepare the irp for the port driver
8785     //
8786     IoSetCompletionRoutine(irp2,
8787                            ClassHwFirmwareDownloadComplete,
8788                            (firmwareImageBuffer != firmwareDownload->ImageBuffer) ? firmwareImageBuffer : NULL,
8789                            TRUE,
8790                            TRUE,
8791                            TRUE);
8792 
8793     IoSetNextIrpStackLocation(irp2);
8794     newStack = IoGetCurrentIrpStackLocation(irp2);
8795     newStack->DeviceObject = DeviceObject;
8796     newStack->MajorFunction = irpStack->MajorFunction;
8797     newStack->MinorFunction = irpStack->MinorFunction;
8798     newStack->Flags = irpStack->Flags;
8799 
8800 
8801     //
8802     // Mark the master irp as pending - whether the lower level
8803     // driver completes it immediately or not this should allow it
8804     // to go all the way back up.
8805     //
8806     IoMarkIrpPending(Irp);
8807 
8808     //
8809     // Setup the CDB.
8810     //
8811     SrbSetCdbLength(Srb, CDB10GENERIC_LENGTH);
8812     cdb = SrbGetCdb(Srb);
8813     cdb->WRITE_BUFFER.OperationCode = SCSIOP_WRITE_DATA_BUFF;
8814     cdb->WRITE_BUFFER.Mode = SCSI_WRITE_BUFFER_MODE_DOWNLOAD_MICROCODE_WITH_OFFSETS_SAVE_DEFER_ACTIVATE;
8815     cdb->WRITE_BUFFER.ModeSpecific = 0;     //Reserved for Mode 0x0E
8816     cdb->WRITE_BUFFER.BufferID = firmwareDownload->Slot;
8817 
8818     cdb->WRITE_BUFFER.BufferOffset[0] = *((PCHAR)&firmwareDownload->Offset + 2);
8819     cdb->WRITE_BUFFER.BufferOffset[1] = *((PCHAR)&firmwareDownload->Offset + 1);
8820     cdb->WRITE_BUFFER.BufferOffset[2] = *((PCHAR)&firmwareDownload->Offset);
8821 
8822     cdb->WRITE_BUFFER.ParameterListLength[0] = *((PCHAR)&bufferSize + 2);
8823     cdb->WRITE_BUFFER.ParameterListLength[1] = *((PCHAR)&bufferSize + 1);
8824     cdb->WRITE_BUFFER.ParameterListLength[2] = *((PCHAR)&bufferSize);
8825 
8826     //
8827     // Send as a tagged command.
8828     //
8829     SrbSetRequestAttribute(Srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
8830     SrbSetSrbFlags(Srb, SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_QUEUE_ACTION_ENABLE);
8831 
8832     //
8833     // Set timeout value.
8834     //
8835     SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
8836 
8837     //
8838     // This routine uses a completion routine so we don't want to release
8839     // the remove lock until then.
8840     //
8841     status = ClassSendSrbAsynchronous(DeviceObject,
8842                                       Srb,
8843                                       irp2,
8844                                       firmwareImageBuffer,
8845                                       bufferSize,
8846                                       TRUE);
8847 
8848     if (status != STATUS_PENDING) {
8849         //
8850         // If the new irp cannot be sent down, free allocated memory and bail out.
8851         //
8852         if (firmwareImageBuffer != firmwareDownload->ImageBuffer) {
8853             FREE_POOL(firmwareImageBuffer);
8854         }
8855 
8856         //
8857         // If the irp cannot be sent down, the Srb has been freed. NULL it to prevent from freeing it again.
8858         //
8859         Srb = NULL;
8860 
8861         ClassReleaseRemoveLock(DeviceObject, irp2);
8862 
8863         IoFreeIrp(irp2);
8864 
8865         goto Exit_Firmware_Download;
8866     }
8867 
8868     return status;
8869 
8870 Exit_Firmware_Download:
8871 
8872     //
8873     // Release the SyncLock if it's still held.
8874     // This should only happen in the failure path.
8875     //
8876     if (lockHeld) {
8877         KeReleaseInStackQueuedSpinLock(&lockHandle);
8878         lockHeld = FALSE;
8879     }
8880 
8881     //
8882     // Firmware Download request will be failed.
8883     //
8884     NT_ASSERT(!NT_SUCCESS(status));
8885 
8886     Irp->IoStatus.Status = status;
8887 
8888 #else
8889     status = STATUS_NOT_IMPLEMENTED;
8890     Irp->IoStatus.Status = status;
8891 #endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8892 
8893     ClassReleaseRemoveLock(DeviceObject, Irp);
8894     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8895 
8896     FREE_POOL(Srb);
8897 
8898     return status;
8899 }
8900 
8901 NTSTATUS
ClassDeviceHwFirmwareActivateProcess(_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_Inout_ PSCSI_REQUEST_BLOCK Srb)8902 ClassDeviceHwFirmwareActivateProcess(
8903     _In_ PDEVICE_OBJECT DeviceObject,
8904     _Inout_ PIRP Irp,
8905     _Inout_ PSCSI_REQUEST_BLOCK Srb
8906     )
8907 {
8908     NTSTATUS status = STATUS_SUCCESS;
8909 
8910 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
8911 
8912     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8913     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
8914 
8915     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
8916     PSTORAGE_HW_FIRMWARE_ACTIVATE firmwareActivate = (PSTORAGE_HW_FIRMWARE_ACTIVATE)Irp->AssociatedIrp.SystemBuffer;
8917     BOOLEAN passDown = FALSE;
8918     PCDB cdb = NULL;
8919     ULONG   i;
8920     BOOLEAN lockHeld = FALSE;
8921     KLOCK_QUEUE_HANDLE lockHandle;
8922 
8923 
8924     //
8925     // Input buffer is not big enough to contain required input information.
8926     //
8927     if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HW_FIRMWARE_ACTIVATE)) {
8928 
8929         status = STATUS_INFO_LENGTH_MISMATCH;
8930         goto Exit_Firmware_Activate;
8931     }
8932 
8933     //
8934     // Input buffer basic validation.
8935     //
8936     if ((firmwareActivate->Version < sizeof(STORAGE_HW_FIRMWARE_ACTIVATE)) ||
8937         (firmwareActivate->Size > irpStack->Parameters.DeviceIoControl.InputBufferLength)) {
8938 
8939         status = STATUS_INVALID_PARAMETER;
8940         goto Exit_Firmware_Activate;
8941     }
8942 
8943     //
8944     // Only process the request for a supported port driver.
8945     //
8946     if (!ClassDeviceHwFirmwareIsPortDriverSupported(DeviceObject)) {
8947         status = STATUS_NOT_IMPLEMENTED;
8948         goto Exit_Firmware_Activate;
8949     }
8950 
8951     //
8952     // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation
8953     // of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet.
8954     //
8955     if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) {
8956 
8957         status = STATUS_UNSUCCESSFUL;
8958         goto Exit_Firmware_Activate;
8959     }
8960 
8961     //
8962     // Process the situation that request should be forwarded to lower level.
8963     //
8964     if (!commonExtension->IsFdo) {
8965         passDown = TRUE;
8966     }
8967 
8968     if ((firmwareActivate->Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER) != 0) {
8969         passDown = TRUE;
8970     }
8971 
8972     if (passDown) {
8973 
8974         IoCopyCurrentIrpStackLocationToNext(Irp);
8975 
8976         ClassReleaseRemoveLock(DeviceObject, Irp);
8977         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8978         FREE_POOL(Srb);
8979         return status;
8980     }
8981 
8982     //
8983     // If firmware information hasn't been cached in classpnp, retrieve it.
8984     //
8985     if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
8986         if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
8987             status = STATUS_NOT_IMPLEMENTED;
8988             goto Exit_Firmware_Activate;
8989         } else {
8990             //
8991             // If this is the first time of retrieving firmware information,
8992             // send request to lower level to get it.
8993             //
8994             status = ClasspGetHwFirmwareInfo(DeviceObject);
8995 
8996             if (!NT_SUCCESS(status)) {
8997                 goto Exit_Firmware_Activate;
8998             }
8999         }
9000     }
9001 
9002     //
9003     // Fail the request if the firmware information cannot be retrieved.
9004     //
9005     if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
9006         if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
9007             status = STATUS_NOT_IMPLEMENTED;
9008         } else {
9009             status = STATUS_UNSUCCESSFUL;
9010         }
9011 
9012         goto Exit_Firmware_Activate;
9013     }
9014 
9015     //
9016     // Acquire the SyncLock to ensure the HwFirmwareInfo pointer doesn't change
9017     // while we're dereferencing it.
9018     //
9019     lockHeld = TRUE;
9020     KeAcquireInStackQueuedSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
9021 
9022     //
9023     // Validate the device support
9024     //
9025     if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SupportUpgrade == FALSE) {
9026         status = STATUS_NOT_SUPPORTED;
9027         goto Exit_Firmware_Activate;
9028     }
9029 
9030     //
9031     // Check if the slot number is valid.
9032     //
9033     for (i = 0; i < fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount; i++) {
9034         if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Slot[i].SlotNumber == firmwareActivate->Slot) {
9035             break;
9036         }
9037     }
9038 
9039     if (i >= fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount) {
9040         //
9041         // Either the slot number is out of scope or the slot is read-only.
9042         //
9043         status = STATUS_INVALID_PARAMETER;
9044         goto Exit_Firmware_Activate;
9045     }
9046 
9047     //
9048     // We're done accessing HwFirmwareInfo at this point so we can release
9049     // the SyncLock.
9050     //
9051     NT_ASSERT(lockHeld);
9052     KeReleaseInStackQueuedSpinLock(&lockHandle);
9053     lockHeld = FALSE;
9054 
9055     //
9056     // Process the request by translating it into WRITE BUFFER command.
9057     //
9058     //
9059     // Setup the CDB. This should be an untagged request.
9060     //
9061     SrbSetCdbLength(Srb, CDB10GENERIC_LENGTH);
9062     cdb = SrbGetCdb(Srb);
9063     cdb->WRITE_BUFFER.OperationCode = SCSIOP_WRITE_DATA_BUFF;
9064     cdb->WRITE_BUFFER.Mode = SCSI_WRITE_BUFFER_MODE_ACTIVATE_DEFERRED_MICROCODE;
9065     cdb->WRITE_BUFFER.ModeSpecific = 0;                     //Reserved for Mode 0x0F
9066     cdb->WRITE_BUFFER.BufferID = firmwareActivate->Slot;    //NOTE: this field will be ignored by SCSI device.
9067 
9068     //
9069     // Set timeout value.
9070     //
9071     SrbSetTimeOutValue(Srb, FIRMWARE_ACTIVATE_TIMEOUT_VALUE);
9072 
9073     //
9074     // This routine uses a completion routine - ClassIoComplete() so we don't want to release
9075     // the remove lock until then.
9076     //
9077     status = ClassSendSrbAsynchronous(DeviceObject,
9078                                       Srb,
9079                                       Irp,
9080                                       NULL,
9081                                       0,
9082                                       FALSE);
9083 
9084     if (status != STATUS_PENDING) {
9085         //
9086         // If the irp cannot be sent down, the Srb has been freed. NULL it to prevent from freeing it again.
9087         //
9088         Srb = NULL;
9089 
9090         goto Exit_Firmware_Activate;
9091     }
9092 
9093     return status;
9094 
9095 Exit_Firmware_Activate:
9096 
9097     //
9098     // Release the SyncLock if it's still held.
9099     // This should only happen in the failure path.
9100     //
9101     if (lockHeld) {
9102         KeReleaseInStackQueuedSpinLock(&lockHandle);
9103         lockHeld = FALSE;
9104     }
9105 
9106     //
9107     // Firmware Activate request will be failed.
9108     //
9109     NT_ASSERT(!NT_SUCCESS(status));
9110 
9111     Irp->IoStatus.Status = status;
9112 
9113 #else
9114     status = STATUS_NOT_IMPLEMENTED;
9115     Irp->IoStatus.Status = status;
9116 #endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
9117 
9118     ClassReleaseRemoveLock(DeviceObject, Irp);
9119     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
9120 
9121     FREE_POOL(Srb);
9122     return status;
9123 }
9124 
9125 
9126 BOOLEAN
ClasspIsThinProvisioningError(_In_ PSCSI_REQUEST_BLOCK Srb)9127 ClasspIsThinProvisioningError (
9128     _In_ PSCSI_REQUEST_BLOCK Srb
9129     )
9130 /*++
9131 
9132 Routine Description:
9133 
9134     This routine checks whether the completed SRB Srb was completed with a thin provisioning
9135     soft threshold error.
9136 
9137 Arguments:
9138 
9139     Srb - the SRB to be checked.
9140 
9141 Return Value:
9142 
9143     BOOLEAN
9144 
9145 --*/
9146 {
9147     if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
9148         PVOID senseBuffer = SrbGetSenseInfoBuffer(Srb);
9149         if (senseBuffer) {
9150             UCHAR senseKey = 0;
9151             UCHAR addlSenseCode = 0;
9152             UCHAR addlSenseCodeQual = 0;
9153             BOOLEAN validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
9154                                                          SrbGetSenseInfoBufferLength(Srb),
9155                                                          SCSI_SENSE_OPTIONS_NONE,
9156                                                          &senseKey,
9157                                                          &addlSenseCode,
9158                                                          &addlSenseCodeQual);
9159 
9160             return (validSense
9161                     && (senseKey == SCSI_SENSE_UNIT_ATTENTION)
9162                     && (addlSenseCode == SCSI_ADSENSE_LB_PROVISIONING)
9163                     && (addlSenseCodeQual == SCSI_SENSEQ_SOFT_THRESHOLD_REACHED));
9164         }
9165     }
9166     return FALSE;
9167 }
9168