ReactOS 0.4.15-dev-5893-g1bb4167
utils.c
Go to the documentation of this file.
1/*++
2
3Copyright (C) Microsoft Corporation, 1991 - 2010
4
5Module Name:
6
7 utils.c
8
9Abstract:
10
11 SCSI class driver routines
12
13Environment:
14
15 kernel mode only
16
17Notes:
18
19
20Revision 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
72VOID
73NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
74ClassGetDeviceParameter(
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
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
231NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
232ClassSetDeviceParameter(
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
311VOID
312NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
313ClassScanForSpecial(
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
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
431VOID
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
485VOID
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{
618 IoFreeMdl(Mdl);
619}
620
621
623{
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
672ClasspDuidGetDeviceIdProperty
673
674Routine Description:
675
676 Add StorageDeviceIdProperty to the device unique ID structure.
677
678Arguments:
679
680 DeviceObject - a pointer to the device object
681 Irp - a pointer to the I/O request packet
682
683Return Value:
684
685 Status Code
686
687--*/
691 IN PIRP Irp
692 )
693{
694 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
695 PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL;
699 PUCHAR dest;
700
702
704
705 ULONG queryLength;
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
754FnExit:
755
756 FREE_POOL(deviceIdDescriptor);
757
758 return status;
759}
760
761
762
763/*++
764
765ClasspDuidGetDeviceProperty
766
767Routine Description:
768
769 Add StorageDeviceProperty to the device unique ID structure.
770
771Arguments:
772
773 DeviceObject - a pointer to the device object
774 Irp - a pointer to the I/O request packet
775
776Return Value:
777
778 Status Code
779
780--*/
784 PIRP Irp
785 )
786{
787 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
788 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = fdoExtension->DeviceDescriptor;
792 PUCHAR dest;
793
795
796 ULONG queryLength;
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
849FnExit:
850
851 return status;
852}
853
854
855/*++
856
857ClasspDuidGetDriveLayout
858
859Routine Description:
860
861 Add drive layout signature to the device unique ID structure.
862 Layout signature is only added for disk-type devices.
863
864Arguments:
865
866 DeviceObject - a pointer to the device object
867 Irp - a pointer to the I/O request packet
868
869Return Value:
870
871 Status Code
872
873--*/
877 PIRP Irp
878 )
879{
880 PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
884 PSTORAGE_DEVICE_LAYOUT_SIGNATURE driveLayoutSignature;
885
887
888 ULONG queryLength;
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
969FnExit:
970
971 FREE_POOL(layoutEx);
972
973 return status;
974}
975
976
977/*++
978
979ClasspDuidQueryProperty
980
981Routine Description:
982
983 Handles IOCTL_STORAGE_QUERY_PROPERTY for device unique ID requests
984 (when PropertyId is StorageDeviceUniqueIdProperty).
985
986Arguments:
987
988 DeviceObject - a pointer to the device object
989 Irp - a pointer to the I/O request packet
990
991Return Value:
992
993 Status Code
994
995--*/
999 PIRP Irp
1000 )
1001{
1002 PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
1003 PSTORAGE_DESCRIPTOR_HEADER descHeader;
1005
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
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
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
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
1125 overflow = TRUE;
1126 }
1127
1128 if (NT_SUCCESS(status)) {
1129 infoFound = TRUE;
1130 }
1131
1132FnExit:
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
1180ClasspWriteCacheProperty()
1181
1182Routine Description:
1183
1184 This routine reads the caching mode page from the device to
1185 build the Write Cache property page.
1186
1187Arguments:
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
1195Return Value:
1196
1197--*/
1198
1201 _In_ PIRP Irp,
1203 )
1204{
1205 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1207 PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
1209 PMODE_PARAMETER_HEADER modeData = NULL;
1210 PMODE_CACHING_PAGE pageData = NULL;
1213 PCDB cdb;
1214
1215 //
1216 // Must run at less then dispatch.
1217 //
1218
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
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
1466WriteCacheExit:
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
1482ULONG
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
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
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{
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;
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
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 //
1639 }
1640 } else {
1642 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
1644 }
1645
1646 //prepare the Srb
1647 if (NT_SUCCESS(status))
1648 {
1649
1650 SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
1654
1655 SrbSetCdbLength(Srb, 16);
1656
1657 cdb = SrbGetCdb(Srb);
1658 cdb->READ_CAPACITY16.OperationCode = SCSIOP_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
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
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
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
1872Exit:
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
1886static
1889 _In_ USHORT MediumRotationRate,
1891 )
1892{
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
1921 )
1922/*++
1923
1924Routine 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
1930Arguments:
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
1936Return 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
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",
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
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",
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",
2067 status));
2068
2069 return status;
2070}
2071
2075 )
2076/*
2077Routine 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
2082Arguments:
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
2087Return 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());
2110 allocationBufferLength,
2111 '5CcS'
2112 );
2113#else
2114
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
2164Exit:
2165 if (dataBuffer != NULL) {
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;
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
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
2336Exit:
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
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;
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
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 //
2424 }
2425 } else {
2427 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
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",
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
2514Exit:
2516
2517 return status;
2518}
2519
2520
2524 _In_ ULONG SrbSize,
2525 _Out_ PCLASS_VPD_B0_DATA BlockLimitsData
2526 )
2527{
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 //
2604 }
2605 } else {
2607 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2610 }
2611
2612 if (NT_SUCCESS(status)) {
2613 // prepare the Srb
2614 SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
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
2723Exit:
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
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
2880Exit:
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
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
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 //
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 {
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;
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 //
3025 lbpDescr->MaxUnmapLbaCount = blockLimitsData.MaxUnmapLbaCount;
3026 lbpDescr->MaxUnmapBlockDescriptorCount = blockLimitsData.MaxUnmapBlockDescrCount;
3027 }
3028#endif
3029 }
3030
3031Exit:
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
3046VOID
3051 _In_ ULONG MaxBlockDescrIndex,
3055 )
3056/*++
3057
3058Routine 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
3068Arguments:
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
3092Return 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) &&
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
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
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
3176Routine Description:
3177
3178 Process TRIM request that received from upper layer.
3179
3180Arguments:
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
3190Return Value:
3191
3192 status of the operation
3193
3194--*/
3195{
3197
3199 PUNMAP_BLOCK_DESCRIPTOR blockDescrPointer;
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 //
3319#else
3321#endif
3322
3323 if (buffer == NULL) {
3325 goto Exit;
3326 }
3327
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 //
3413 break;
3414 }
3415
3416 } else {
3418 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
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 //
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;
3466 }
3467 }
3468
3469Exit:
3470
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
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",
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",
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
3755Exit:
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
3775 _In_ ULONGLONG StartingLBA,
3776 _Inout_ PLBA_STATUS_LIST_HEADER LBAStatusHeader,
3777 _In_ ULONG LBAStatusSize,
3778 _In_ BOOLEAN ConsolidateableBlocksOnly
3779 )
3780/*++
3781
3782Routine Description:
3783
3784 Send down a Get LBA Status command for the given range.
3785
3786Arguments:
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
3795Return Value:
3796
3797 Status of the operation.
3798
3799--*/
3800{
3802 PCDB cdb;
3803
3804 if (LBAStatusHeader == NULL || LBAStatusSize == 0)
3805 {
3807 }
3808
3809 //
3810 // Build and send down the Get LBA Status command.
3811 //
3812 SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
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
3865 )
3866/*
3867Routine 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
3889Arguments:
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
3896Return 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;
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
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
4070Exit:
4071 Irp->IoStatus.Information = information;
4072 Irp->IoStatus.Status = finalStatus;
4075 return finalStatus;
4076}
4077
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/*
4092Routine 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
4114Arguments:
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
4132Return 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 {
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",
4234 LengthInBytes,
4236 alignmentInBytes,
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",
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)
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",
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",
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",
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",
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)
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)
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 //
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",
4672 totalProcessedSlabs));
4673Exit:
4674
4675 FREE_POOL(lbaStatusListHeader);
4676 return status;
4677}
4678
4682 _In_ ULONG LogPageSize,
4684 )
4685/*
4686Routine 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
4691Arguments:
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
4699Return 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",
4725 LogPage,
4726 LogPageSize));
4727
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 //
4747 }
4748 } else {
4750 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
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",
4794 status
4795 ));
4796
4797 return status;
4798}
4799
4802 _In_ ULONG LogPageSize,
4804 _In_ ULONG ResourcesSize,
4806 )
4807/*
4808Routine 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
4819Arguments:
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
4827Return 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",
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",
4872 Resources,
4873 ResourcesSize,
4874 LogPage,
4875 LogPageSize));
4876
4878 }
4879
4880 //
4881 // Calculate the threshold set size (in LBAs).