ReactOS  0.4.15-dev-1152-g6c94e4f
utils.c
Go to the documentation of this file.
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!
52 {
53  ULONG length; // strlen returns an int, not size_t (!)
54  PAGED_CODE();
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 
72 VOID
73 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
74 ClassGetDeviceParameter(
78  _Inout_ PULONG ParameterValue // also default value
79  )
80 {
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 
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,
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 
124  queryTable->Name = ParameterName;
125  queryTable->EntryContext = ParameterValue;
127  queryTable->DefaultData = NULL;
128  queryTable->DefaultLength = 0;
129 
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 
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,
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 
187  queryTable->Name = ParameterName;
188  queryTable->EntryContext = ParameterValue;
190  queryTable->DefaultData = NULL;
191  queryTable->DefaultLength = 0;
192 
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 
230 NTSTATUS
231 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
232 ClassSetDeviceParameter(
237 {
239  HANDLE deviceParameterHandle = NULL;
240  HANDLE deviceSubkeyHandle = NULL;
241 
242  PAGED_CODE();
243 
244  //
245  // open the given parameter
246  //
247 
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,
262  deviceParameterHandle,
263  NULL);
264 
265  status = ZwCreateKey(&deviceSubkeyHandle,
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 
279  (PWSTR) (SubkeyName ?
280  deviceSubkeyHandle :
281  deviceParameterHandle),
283  REG_DWORD,
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 
311 VOID
312 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
313 ClassScanForSpecial(
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();
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
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 
460 
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 
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
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,
549  CLEAR_FLAG(FdoExtension->SrbFlags,
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,
561  CLEAR_FLAG(FdoExtension->SrbFlags,
563  }
564  if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
566  SET_FLAG(FdoExtension->SrbFlags,
568  }
569  if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
571  SET_FLAG(FdoExtension->SrbFlags,
573  }
574  }
575  } // end of threshhold definitely being hit for first time
576 
577  KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
578  return;
579 }
580 
581 
583 {
584  PMDL mdl;
585 
586  mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL);
587  if (mdl){
588  _SEH2_TRY {
590 #ifdef _MSC_VER
591  #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
592 #endif
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 
610 {
611  return ClasspBuildDeviceMdl(Buffer, BufferLen, FALSE);
612 }
613 
614 
616 {
617  MmUnlockPages(Mdl);
618  IoFreeMdl(Mdl);
619 }
620 
621 
623 {
624  ClasspFreeDeviceMdl(Mdl);
625  return;
626 }
627 
628 
629 #if 0
630  VOID
631  ClasspPerfResetCounters(
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,
646  CLEAR_FLAG(FdoExtension->SrbFlags,
648  }
649  if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
651  CLEAR_FLAG(FdoExtension->SrbFlags,
653  }
654  if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
656  SET_FLAG(FdoExtension->SrbFlags,
658  }
659  if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
661  SET_FLAG(FdoExtension->SrbFlags,
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
691  IN PIRP Irp
692  )
693 {
694  PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
695  PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL;
697  PSTORAGE_DESCRIPTOR_HEADER descHeader;
699  PUCHAR dest;
700 
702 
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);
741  goto FnExit;
742  }
743 
744  storageDuid = Irp->AssociatedIrp.SystemBuffer;
745  storageDuid->StorageDeviceIdOffset = offset;
746 
748  deviceIdDescriptor,
749  deviceIdDescriptor->Size);
750 
751  Irp->IoStatus.Information = storageDuid->Size;
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
784  PIRP Irp
785  )
786 {
787  PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
788  PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = fdoExtension->DeviceDescriptor;
790  PSTORAGE_DESCRIPTOR_HEADER descHeader;
792  PUCHAR dest;
793 
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);
836  goto FnExit;
837  }
838 
839  storageDuid = Irp->AssociatedIrp.SystemBuffer;
840  storageDuid->StorageDeviceOffset = offset;
841 
843  deviceDescriptor,
844  deviceDescriptor->Size);
845 
846  Irp->IoStatus.Information = storageDuid->Size;
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
877  PIRP Irp
878  )
879 {
882  PSTORAGE_DESCRIPTOR_HEADER descHeader;
884  PSTORAGE_DEVICE_LAYOUT_SIGNATURE driveLayoutSignature;
885 
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 
905 
906  if (!NT_SUCCESS(status)) {
908  goto FnExit;
909  }
910 
911  if (layoutEx->PartitionStyle != PARTITION_STYLE_GPT &&
912  layoutEx->PartitionStyle != PARTITION_STYLE_MBR) {
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);
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;
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
999  PIRP Irp
1000  )
1001 {
1002  PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
1003  PSTORAGE_DESCRIPTOR_HEADER descHeader;
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 
1023  goto FnExit;
1024  }
1025 
1026  //
1027  // Check proper query type.
1028  //
1029 
1030  if (query->QueryType == PropertyExistsQuery) {
1031  Irp->IoStatus.Information = 0;
1033  goto FnExit;
1034  }
1035 
1036  if (query->QueryType != PropertyStandardQuery) {
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 {
1051  goto FnExit;
1052  }
1053 
1054  //
1055  // Verify output parameters.
1056  //
1057 
1058  if (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
1059 
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 
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 
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 
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)) {
1150  Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
1151  } else {
1153  }
1154 
1155  } else if (infoFound) {
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 {
1167  }
1168  }
1169 
1170  Irp->IoStatus.Status = status;
1171 
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 
1201  _In_ PIRP Irp,
1203  )
1204 {
1205  PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1206  PSTORAGE_WRITE_CACHE_PROPERTY writeCache;
1207  PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
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 
1223  goto WriteCacheExit;
1224  }
1225 
1226  //
1227  // Check proper query type.
1228  //
1229 
1230  if (query->QueryType == PropertyExistsQuery) {
1232  goto WriteCacheExit;
1233  }
1234 
1235  if (query->QueryType != PropertyStandardQuery) {
1237  goto WriteCacheExit;
1238  }
1239 
1240  length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
1241 
1242  if (length < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
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)) {
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);
1281  SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
1282 
1283  SrbSetCdbLength(Srb, 10);
1284  cdb = SrbGetCdb(Srb);
1285  cdb->CDB10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
1286 
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  //
1305  }
1306 
1307  modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
1310 
1311  if (modeData == NULL) {
1312  TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to allocate mode data buffer\n"));
1314  goto WriteCacheExit;
1315  }
1316 
1318 
1320  (PCHAR) modeData,
1323 
1324  if (length < sizeof(MODE_PARAMETER_HEADER)) {
1325 
1326  //
1327  // Retry the request in case of a check condition.
1328  //
1329 
1331  (PCHAR) modeData,
1334 
1335  if (length < sizeof(MODE_PARAMETER_HEADER)) {
1336  TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Mode Sense failed\n"));
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,
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 ?
1375 
1376  writeCache->WriteCacheType = pageData->WriteCacheEnable ?
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) &&
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 
1398 
1400  (PCHAR) modeData,
1404 
1405  if (length < sizeof(MODE_PARAMETER_HEADER)) {
1406 
1407  //
1408  // Retry the request in case of a check condition.
1409  //
1410 
1412  (PCHAR) modeData,
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;
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,
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 ?
1462  }
1463 
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 
1478 
1479  return status;
1480 }
1481 
1482 ULONG
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
1519  _In_ PREAD_CAPACITY16_DATA ReadCapacity16Data
1520  )
1521 {
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
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
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 {
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 
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.
1619  }
1620 
1621  RtlZeroMemory(dataBuffer, allocationBufferLength);
1622 
1623  //
1624  // Initialize the SRB.
1625  //
1626  if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1630  1,
1632  if (NT_SUCCESS(status)) {
1634  } else {
1635  //
1636  // Should not occur.
1637  //
1638  NT_ASSERT(FALSE);
1639  }
1640  } else {
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);
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 
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.
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  {
1684  }
1685 
1686  if (NT_SUCCESS(status))
1687  {
1688  // cache data into FdoExtension
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 
1702  _In_ PIRP Irp,
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 {
1711 
1712  PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1714  PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
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) {
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.
1736 
1738  status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1739  return status;
1740  }
1741 
1742  //
1743  // Check proper query type.
1744  //
1745 
1746  if (query->QueryType == PropertyExistsQuery) {
1748  goto Exit;
1749  } else if (query->QueryType != PropertyStandardQuery) {
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) {
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 
1771 
1772  if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
1773 
1775  accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1776  accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
1778  goto Exit;
1779  }
1780 
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 
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
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 
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.
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 
1882 
1883  return status;
1884 }
1885 
1886 __inline
1887 NTSTATUS
1889  _In_ USHORT MediumRotationRate,
1891  )
1892 {
1893  NTSTATUS status;
1894 
1895  if (MediumRotationRate == 0x0001) {
1896  // Non-rotating media (e.g., solid state device)
1899  } else if ( (MediumRotationRate >= 0x401) &&
1900  (MediumRotationRate <= 0xFFFE) ) {
1901  // Nominal media rotation rate in rotations per minute (rpm)
1904  } else {
1905  // Unknown cases:
1906  // 0 - Rate not reported
1907  // 0002h-0400h - Reserved
1908  // FFFFh - Reserved
1910  }
1911 
1912  return status;
1913 }
1914 
1915 
1916 NTSTATUS
1919  _Inout_ PIRP Irp,
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 {
1944  PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)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  //
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 
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 
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 
1996  goto __ClasspDeviceMediaTypeProperty_Exit;
1997  }
1998 
1999  length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
2000 
2001  if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2002 
2004  pDesc->Version = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
2005  pDesc->Size = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
2006  } else {
2007 
2009  goto __ClasspDeviceMediaTypeProperty_Exit;
2010  }
2011 
2013 
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) {
2023  } else {
2024  //
2025  // Otherwise device was previously found lacking support for this VPD page. Fail the request.
2026  //
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;
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 
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 
2073  _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension,
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 {
2097  PCDB cdb;
2098  UCHAR bufferLength = sizeof(VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE); // data is 64 bytes
2099  ULONG allocationBufferLength = bufferLength;
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 
2116  bufferLength,
2117  '5CcS'
2118  );
2119 #endif
2120  if (dataBuffer == NULL) {
2122  goto Exit;
2123  }
2124 
2125  RtlZeroMemory(dataBuffer, allocationBufferLength);
2126 
2127  // prepare the Srb
2128  SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
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
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.
2150  } else if ( (dataBuffer->PageLength != 0x3C) || (dataBuffer->PageCode != VPD_BLOCK_DEVICE_CHARACTERISTICS) ) {
2151  // 'PageLength' shall be 0x3C; and 'PageCode' shall match.
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 
2174  _In_ PIRP Irp,
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 {
2183 
2184  PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2186  PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
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.
2199 
2201  status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
2202  return status;
2203  }
2204 
2205  //
2206  // Check proper query type.
2207  //
2208 
2209  if (query->QueryType == PropertyExistsQuery) {
2211  goto Exit;
2212  } else if (query->QueryType != PropertyStandardQuery) {
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) {
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 
2234 
2235  if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2236 
2238  seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2239  seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
2241  goto Exit;
2242  }
2243 
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 
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) {
2263  } else {
2264  // the INQUIRY - VPD page command to discover the info is not supported, fail the 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;
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;
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 
2346 
2347  return status;
2348 }
2349 
2353  )
2354 {
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.
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) {
2415  1,
2417  if (NT_SUCCESS(status)) {
2419  } else {
2420  //
2421  // Should not occur.
2422  //
2423  NT_ASSERT(FALSE);
2424  }
2425  } else {
2427  Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2428  Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2430  }
2431 
2432  if (NT_SUCCESS(status)) {
2433  // prepare the Srb
2434  SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
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 
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  {
2463  }
2464 
2465  if (NT_SUCCESS(status)) {
2466  REVERSE_BYTES_SHORT(&pageLength, &(lbProvisioning->PageLength));
2467  }
2468 
2469  if ( NT_SUCCESS(status) &&
2470  ((dataTransferLength < 0x08) ||
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.
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 {
2510  }
2511 
2512  fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = status;
2513 
2514 Exit:
2515  FREE_POOL(dataBuffer);
2516 
2517  return status;
2518 }
2519 
2520 
2524  _In_ ULONG SrbSize,
2525  _Out_ PCLASS_VPD_B0_DATA BlockLimitsData
2526  )
2527 {
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.
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
2593  SrbSize,
2594  1,
2596 
2597  if (NT_SUCCESS(status)) {
2599  } else {
2600  //
2601  // Should not occur.
2602  //
2603  NT_ASSERT(FALSE);
2604  }
2605  } else {
2607  Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2608  Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2610  }
2611 
2612  if (NT_SUCCESS(status)) {
2613  // prepare the Srb
2614  SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
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 
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  {
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) ||
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.
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 {
2719  }
2720 
2721  BlockLimitsData->CommandStatus = status;
2722 
2723 Exit:
2724  FREE_POOL(dataBuffer);
2725 
2726  return status;
2727 }
2728 
2729 
2732  _In_ PIRP Irp,
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 {
2741 
2742  PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
2744  PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
2746  ULONG length = 0;
2747  ULONG information = 0;
2748 
2749  PDEVICE_TRIM_DESCRIPTOR trimDescr;
2750 
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.
2759 
2761  status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
2762  return status;
2763  }
2764 
2765  //
2766  // Check proper query type.
2767  //
2768 
2769  if (query->QueryType == PropertyExistsQuery) {
2771  goto Exit;
2772  } else if (query->QueryType != PropertyStandardQuery) {
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) {
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 
2798  trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
2799  trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
2801  goto Exit;
2802  }
2803 
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) ||
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
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 
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 
2890 
2891  return status;
2892 }
2893 
2896  _Inout_ PIRP Irp,
2898  )
2899 {
2901  NTSTATUS blockLimitsStatus;
2903  PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
2905  ULONG length = 0;
2906  ULONG information = 0;
2907  CLASS_VPD_B0_DATA blockLimitsData;
2908  ULONG generationCount;
2909 
2911 
2913 
2914  //
2915  // Check proper query type.
2916  //
2917  if (query->QueryType == PropertyExistsQuery) {
2919  goto Exit;
2920  } else if (query->QueryType != PropertyStandardQuery) {
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) {
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 
2943 
2944  if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
2945 
2947  lbpDescr->Version = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2948  lbpDescr->Size = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2950  goto Exit;
2951  }
2952 
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)) {
2966  } else {
2967  lbpDescr->Version = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
2968  lbpDescr->Size = 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  {
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 
3041 
3042  return status;
3043 }
3044 
3045 
3046 VOID
3051  _In_ ULONG MaxBlockDescrIndex,
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;
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) {
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  //
3138  }
3139 
3140  REVERSE_BYTES_QUAD(BlockDescr[*CurrentBlockDescrIndex].StartingLba, &startingSector);
3142 
3143  DataSetRange->StartingOffset += sectorCount * FdoExtension->DiskGeometry.BytesPerSector;
3144  DataSetRange->LengthInBytes -= sectorCount * FdoExtension->DiskGeometry.BytesPerSector;
3145 
3146  *CurrentBlockDescrIndex += 1;
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
3166  _In_ PDEVICE_DATA_SET_RANGE DataSetRanges,
3167  _In_ ULONG DataSetRangesCount,
3168  _In_ ULONG UnmapGranularity,
3169  _In_ ULONG SrbFlags,
3170  _In_ PIRP Irp,
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 {
3197 
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);
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);
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);
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
3321 #endif
3322 
3323  if (buffer == NULL) {
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 
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);
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) {
3404  1,
3406  if (NT_SUCCESS(status)) {
3408  } else {
3409  //
3410  // Should not occur.
3411  //
3412  NT_ASSERT(FALSE);
3413  break;
3414  }
3415 
3416  } else {
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);
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);
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 
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 
3478  _In_ PIRP Irp,
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 {
3489 
3490  PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_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.
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 
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) {
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)) ||
3545  (dsmAttributes->DataSetRangesOffset != 0 ||
3546  dsmAttributes->DataSetRangesLength != 0))) {
3547 
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 
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) {
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 
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 
3692  goto Exit;
3693  }
3694  }
3695  }
3696 
3697 
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 
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 
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 
3767 
3768  return status;
3769 }
3770 
3771 NTSTATUS
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 {
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);
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 
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  {
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 
3863  _Inout_ PIRP Irp,
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 {
3914  PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer;
3915  PDEVICE_DATA_SET_RANGE dataSetRanges = NULL;
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  //
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) &&
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 
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;
4075  return finalStatus;
4076 }
4077 
4078 NTSTATUS
4081  _In_ PCLASS_VPD_B0_DATA BlockLimitsData,
4083  _In_ ULONGLONG LengthInBytes,
4085  _Inout_ PULONG DsmOutputLength,
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 {
4149 
4151 
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  //
4183  {
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  {
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,
4236  alignmentInBytes,
4237  (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart));
4238 
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 
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)
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)
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  {
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);
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]) -
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;
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;
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;
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) {
4627 
4628  lbpStateV2->SlabSizeInBytes = bytesPerSlab;
4629  lbpStateV2->SlabOffsetDeltaInBytes = startingOffsetDelta;
4630  lbpStateV2->SlabAllocationBitMapBitCount = totalProcessedSlabs;
4631  lbpStateV2->SlabAllocationBitMapLength = ((totalProcessedSlabs - 1) / (ULONGLONG)bitMapGranularityInBits) + 1;
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;
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 
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 
4682  _In_ ULONG LogPageSize,
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 {
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) {
4738  1,
4740  if (NT_SUCCESS(status)) {
4742  } else {
4743  //
4744  // Should not occur.
4745  //
4746  NT_ASSERT(FALSE);
4747  }
4748  } else {
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);
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 
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  {
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 
4802  _In_ ULONG LogPageSize,
4804  _In_ ULONG ResourcesSize,
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 {
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  //
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 
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  {
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 
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 
4961  _In_ ULONG ResourcesSize,
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;
4998  ULONG logPageSize;
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  //
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
5048 #endif
5049  if (logPage != NULL)
5050  {
5051  //
5052  // Get the LBP log page from the device.
5053  //
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  //
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 
5081  }
5082 
5083  return status;
5084 }
5085 
5086 NTSTATUS
5089  _Inout_ PIRP Irp,
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;
5116  PSTORAGE_LB_PROVISIONING_MAP_RESOURCES mapResources = (PSTORAGE_LB_PROVISIONING_MAP_RESOURCES)Irp->AssociatedIrp.SystemBuffer;
5117 
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;
5132 
5133  return status;
5134 }
5135 
5136 _Function_class_(IO_WORKITEM_ROUTINE)
5139 VOID
5140 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
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 {
5159  PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
5161  PSCSI_REQUEST_BLOCK srb = NULL;
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)) {
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  //
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(