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! 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 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 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 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 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 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 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 609 PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen) 610 { 611 return ClasspBuildDeviceMdl(Buffer, BufferLen, FALSE); 612 } 613 614 615 VOID ClasspFreeDeviceMdl(PMDL Mdl) 616 { 617 MmUnlockPages(Mdl); 618 IoFreeMdl(Mdl); 619 } 620 621 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 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 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) 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 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 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) 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 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) 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 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) 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 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 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 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 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 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 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 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 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) 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) 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) 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 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 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) 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 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 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 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 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 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 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 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 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_ 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 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 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 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