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