1 /* 2 * PROJECT: ReactOS Storage Stack / SCSIPORT storage port library 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: IOCTL handlers 5 * COPYRIGHT: Eric Kohl (eric.kohl@reactos.org) 6 * Aleksey Bragin (aleksey@reactos.org) 7 * 2020 Victor Perevertkin (victor.perevertkin@reactos.org) 8 */ 9 10 #include "scsiport.h" 11 12 #define NDEBUG 13 #include <debug.h> 14 15 16 static 17 NTSTATUS 18 SpiGetInquiryData( 19 _In_ PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, 20 _In_ PIRP Irp) 21 { 22 ULONG InquiryDataSize; 23 ULONG BusCount, Length; 24 PIO_STACK_LOCATION IrpStack; 25 PSCSI_ADAPTER_BUS_INFO AdapterBusInfo; 26 PSCSI_INQUIRY_DATA InquiryData; 27 PUCHAR Buffer; 28 29 DPRINT("SpiGetInquiryData() called\n"); 30 31 /* Get pointer to the buffer */ 32 IrpStack = IoGetCurrentIrpStackLocation(Irp); 33 Buffer = Irp->AssociatedIrp.SystemBuffer; 34 35 /* Initialize bus and LUN counters */ 36 BusCount = DeviceExtension->NumberOfBuses; 37 38 /* Calculate size of inquiry data, rounding up to sizeof(ULONG) */ 39 InquiryDataSize = ALIGN_UP(sizeof(SCSI_INQUIRY_DATA) - 1 + INQUIRYDATABUFFERSIZE, ULONG); 40 41 /* Calculate data size */ 42 Length = sizeof(SCSI_ADAPTER_BUS_INFO) + (BusCount - 1) * sizeof(SCSI_BUS_DATA); 43 44 Length += InquiryDataSize * DeviceExtension->TotalLUCount; 45 46 /* Check, if all data is going to fit into provided buffer */ 47 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < Length) 48 { 49 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; 50 return STATUS_BUFFER_TOO_SMALL; 51 } 52 53 /* Store data size in the IRP */ 54 Irp->IoStatus.Information = Length; 55 56 DPRINT("Data size: %lu\n", Length); 57 58 AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer; 59 60 AdapterBusInfo->NumberOfBuses = (UCHAR)BusCount; 61 62 /* Point InquiryData to the corresponding place inside Buffer */ 63 InquiryData = (PSCSI_INQUIRY_DATA)(Buffer + sizeof(SCSI_ADAPTER_BUS_INFO) + 64 (BusCount - 1) * sizeof(SCSI_BUS_DATA)); 65 66 /* Loop each bus */ 67 for (UINT8 pathId = 0; pathId < DeviceExtension->NumberOfBuses; pathId++) 68 { 69 PSCSI_BUS_DATA BusData = &AdapterBusInfo->BusData[pathId]; 70 71 /* Calculate and save an offset of the inquiry data */ 72 BusData->InquiryDataOffset = (ULONG)((PUCHAR)InquiryData - Buffer); 73 74 /* Store Initiator Bus Id */ 75 BusData->InitiatorBusId = DeviceExtension->Buses[pathId].BusIdentifier; 76 77 /* Store LUN count */ 78 BusData->NumberOfLogicalUnits = DeviceExtension->Buses[pathId].LogicalUnitsCount; 79 80 /* Loop all LUNs */ 81 PSCSI_BUS_INFO bus = &DeviceExtension->Buses[pathId]; 82 83 for (PLIST_ENTRY lunEntry = bus->LunsListHead.Flink; 84 lunEntry != &bus->LunsListHead; 85 lunEntry = lunEntry->Flink) 86 { 87 PSCSI_PORT_LUN_EXTENSION lunExt = 88 CONTAINING_RECORD(lunEntry, SCSI_PORT_LUN_EXTENSION, LunEntry); 89 90 DPRINT("(Bus %lu Target %lu Lun %lu)\n", pathId, lunExt->TargetId, lunExt->Lun); 91 92 /* Fill InquiryData with values */ 93 InquiryData->PathId = lunExt->PathId; 94 InquiryData->TargetId = lunExt->TargetId; 95 InquiryData->Lun = lunExt->Lun; 96 InquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE; 97 InquiryData->DeviceClaimed = lunExt->DeviceClaimed; 98 InquiryData->NextInquiryDataOffset = 99 (ULONG)((PUCHAR)InquiryData + InquiryDataSize - Buffer); 100 101 /* Copy data in it */ 102 RtlCopyMemory(InquiryData->InquiryData, 103 &lunExt->InquiryData, 104 INQUIRYDATABUFFERSIZE); 105 106 /* Move to the next LUN */ 107 InquiryData = (PSCSI_INQUIRY_DATA) ((ULONG_PTR)InquiryData + InquiryDataSize); 108 } 109 110 /* Either mark the end, or set offset to 0 */ 111 if (BusData->NumberOfLogicalUnits != 0) 112 ((PSCSI_INQUIRY_DATA) ((PCHAR)InquiryData - InquiryDataSize))->NextInquiryDataOffset = 0; 113 else 114 BusData->InquiryDataOffset = 0; 115 } 116 117 /* Finish with success */ 118 Irp->IoStatus.Status = STATUS_SUCCESS; 119 return STATUS_SUCCESS; 120 } 121 122 static 123 UINT32 124 GetFieldLength( 125 _In_ PUCHAR Name, 126 _In_ UINT32 MaxLength) 127 { 128 UINT32 Index; 129 UINT32 LastCharacterPosition = 0; 130 131 // scan the field and return last position which contains a valid character 132 for (Index = 0; Index < MaxLength; Index++) 133 { 134 if (Name[Index] != ' ') 135 { 136 // trim white spaces from field 137 LastCharacterPosition = Index; 138 } 139 } 140 141 // convert from zero based index to length 142 return LastCharacterPosition + 1; 143 } 144 145 static 146 NTSTATUS 147 PdoHandleQueryProperty( 148 _In_ PDEVICE_OBJECT DeviceObject, 149 _Inout_ PIRP Irp) 150 { 151 PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); 152 PSCSI_PORT_LUN_EXTENSION lunExt = DeviceObject->DeviceExtension; 153 NTSTATUS status; 154 155 ASSERT(ioStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(STORAGE_PROPERTY_QUERY)); 156 ASSERT(Irp->AssociatedIrp.SystemBuffer); 157 ASSERT(!lunExt->Common.IsFDO); 158 159 PSTORAGE_PROPERTY_QUERY PropertyQuery = Irp->AssociatedIrp.SystemBuffer; 160 161 // check property type 162 if (PropertyQuery->PropertyId != StorageDeviceProperty && 163 PropertyQuery->PropertyId != StorageAdapterProperty) 164 { 165 // only device property / adapter property are supported 166 status = STATUS_INVALID_PARAMETER_1; 167 goto completeIrp; 168 } 169 170 // check query type 171 if (PropertyQuery->QueryType == PropertyExistsQuery) 172 { 173 // device property / adapter property is supported 174 status = STATUS_SUCCESS; 175 goto completeIrp; 176 } 177 178 if (PropertyQuery->QueryType != PropertyStandardQuery) 179 { 180 // only standard query and exists query are supported 181 status = STATUS_INVALID_PARAMETER_2; 182 goto completeIrp; 183 } 184 185 switch (PropertyQuery->PropertyId) 186 { 187 case StorageDeviceProperty: 188 { 189 PINQUIRYDATA inquiryData = &lunExt->InquiryData; 190 191 // compute extra parameters length 192 UINT32 FieldLengthVendor = GetFieldLength(inquiryData->VendorId, 8), 193 FieldLengthProduct = GetFieldLength(inquiryData->ProductId, 16), 194 FieldLengthRevision = GetFieldLength(inquiryData->ProductRevisionLevel, 4); 195 196 // total length required is sizeof(STORAGE_DEVICE_DESCRIPTOR) + FieldLength + 4 extra null bytes - 1 197 // -1 due STORAGE_DEVICE_DESCRIPTOR contains one byte length of parameter data 198 UINT32 TotalLength = sizeof(STORAGE_DEVICE_DESCRIPTOR) 199 + FieldLengthVendor 200 + FieldLengthProduct 201 + FieldLengthRevision 202 + 3; 203 204 // check if output buffer is long enough 205 if (ioStack->Parameters.DeviceIoControl.OutputBufferLength < TotalLength) 206 { 207 // buffer too small 208 PSTORAGE_DESCRIPTOR_HEADER DescriptorHeader = Irp->AssociatedIrp.SystemBuffer; 209 ASSERT(ioStack->Parameters.DeviceIoControl.OutputBufferLength >= 210 sizeof(STORAGE_DESCRIPTOR_HEADER)); 211 212 // return required size 213 DescriptorHeader->Version = TotalLength; 214 DescriptorHeader->Size = TotalLength; 215 216 Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER); 217 status = STATUS_SUCCESS; 218 goto completeIrp; 219 } 220 221 // initialize the device descriptor 222 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = Irp->AssociatedIrp.SystemBuffer; 223 224 deviceDescriptor->Version = sizeof(STORAGE_DEVICE_DESCRIPTOR); 225 deviceDescriptor->Size = TotalLength; 226 deviceDescriptor->DeviceType = inquiryData->DeviceType; 227 deviceDescriptor->DeviceTypeModifier = inquiryData->DeviceTypeModifier; 228 deviceDescriptor->RemovableMedia = inquiryData->RemovableMedia; 229 deviceDescriptor->CommandQueueing = inquiryData->CommandQueue; 230 deviceDescriptor->BusType = BusTypeScsi; 231 deviceDescriptor->VendorIdOffset = 232 FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR, RawDeviceProperties); 233 deviceDescriptor->ProductIdOffset = 234 deviceDescriptor->VendorIdOffset + FieldLengthVendor + 1; 235 deviceDescriptor->ProductRevisionOffset = 236 deviceDescriptor->ProductIdOffset + FieldLengthProduct + 1; 237 deviceDescriptor->SerialNumberOffset = 0; 238 deviceDescriptor->RawPropertiesLength = 239 FieldLengthVendor + FieldLengthProduct + FieldLengthRevision + 3; 240 241 // copy descriptors 242 PUCHAR Buffer = deviceDescriptor->RawDeviceProperties; 243 244 RtlCopyMemory(Buffer, inquiryData->VendorId, FieldLengthVendor); 245 Buffer[FieldLengthVendor] = '\0'; 246 Buffer += FieldLengthVendor + 1; 247 248 RtlCopyMemory(Buffer, inquiryData->ProductId, FieldLengthProduct); 249 Buffer[FieldLengthProduct] = '\0'; 250 Buffer += FieldLengthProduct + 1; 251 252 RtlCopyMemory(Buffer, inquiryData->ProductRevisionLevel, FieldLengthRevision); 253 Buffer[FieldLengthRevision] = '\0'; 254 Buffer += FieldLengthRevision + 1; 255 256 DPRINT("Vendor %s\n", 257 (LPCSTR)((ULONG_PTR)deviceDescriptor + deviceDescriptor->VendorIdOffset)); 258 DPRINT("Product %s\n", 259 (LPCSTR)((ULONG_PTR)deviceDescriptor + deviceDescriptor->ProductIdOffset)); 260 DPRINT("Revision %s\n", 261 (LPCSTR)((ULONG_PTR)deviceDescriptor + deviceDescriptor->ProductRevisionOffset)); 262 263 Irp->IoStatus.Information = TotalLength; 264 status = STATUS_SUCCESS; 265 goto completeIrp; 266 } 267 case StorageAdapterProperty: 268 { 269 // forward to the lower device 270 IoSkipCurrentIrpStackLocation(Irp); 271 return IoCallDriver(lunExt->Common.LowerDevice, Irp); 272 } 273 case StorageDeviceIdProperty: 274 { 275 // TODO 276 } 277 default: 278 { 279 UNREACHABLE; 280 status = STATUS_NOT_IMPLEMENTED; 281 goto completeIrp; 282 } 283 } 284 285 completeIrp: 286 Irp->IoStatus.Status = status; 287 IoCompleteRequest(Irp, IO_NO_INCREMENT); 288 return status; 289 } 290 291 static 292 NTSTATUS 293 FdoHandleQueryProperty( 294 _In_ PDEVICE_OBJECT DeviceObject, 295 _Inout_ PIRP Irp) 296 { 297 PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); 298 PSCSI_PORT_DEVICE_EXTENSION portExt = DeviceObject->DeviceExtension; 299 NTSTATUS status; 300 301 ASSERT(ioStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(STORAGE_PROPERTY_QUERY)); 302 ASSERT(Irp->AssociatedIrp.SystemBuffer); 303 ASSERT(portExt->Common.IsFDO); 304 305 PSTORAGE_PROPERTY_QUERY PropertyQuery = Irp->AssociatedIrp.SystemBuffer; 306 307 // check property type (handle only StorageAdapterProperty) 308 if (PropertyQuery->PropertyId != StorageAdapterProperty) 309 { 310 if (PropertyQuery->PropertyId == StorageDeviceProperty || 311 PropertyQuery->PropertyId == StorageDeviceIdProperty) 312 { 313 status = STATUS_INVALID_DEVICE_REQUEST; 314 } 315 else 316 { 317 status = STATUS_INVALID_PARAMETER_1; 318 } 319 320 goto completeIrp; 321 } 322 323 // check query type 324 if (PropertyQuery->QueryType == PropertyExistsQuery) 325 { 326 // device property / adapter property is supported 327 status = STATUS_SUCCESS; 328 goto completeIrp; 329 } 330 331 if (PropertyQuery->QueryType != PropertyStandardQuery) 332 { 333 // only standard query and exists query are supported 334 status = STATUS_INVALID_PARAMETER_2; 335 goto completeIrp; 336 } 337 338 if (ioStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8)) 339 { 340 // buffer too small 341 PSTORAGE_DESCRIPTOR_HEADER DescriptorHeader = Irp->AssociatedIrp.SystemBuffer; 342 ASSERT(ioStack->Parameters.DeviceIoControl.OutputBufferLength 343 >= sizeof(STORAGE_DESCRIPTOR_HEADER)); 344 345 // return required size 346 DescriptorHeader->Version = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8); 347 DescriptorHeader->Size = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8); 348 349 Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER); 350 status = STATUS_SUCCESS; 351 goto completeIrp; 352 } 353 354 // get adapter descriptor, information is returned in the same buffer 355 PSTORAGE_ADAPTER_DESCRIPTOR_WIN8 adapterDescriptor = Irp->AssociatedIrp.SystemBuffer; 356 357 // fill out descriptor 358 // NOTE: STORAGE_ADAPTER_DESCRIPTOR_WIN8 may vary in size, so it's important to zero out 359 // all unused fields 360 *adapterDescriptor = (STORAGE_ADAPTER_DESCRIPTOR_WIN8) { 361 .Version = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8), 362 .Size = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8), 363 .MaximumTransferLength = portExt->PortCapabilities.MaximumTransferLength, 364 .MaximumPhysicalPages = portExt->PortCapabilities.MaximumPhysicalPages, 365 .AlignmentMask = portExt->PortCapabilities.AlignmentMask, 366 .AdapterUsesPio = portExt->PortCapabilities.AdapterUsesPio, 367 .AdapterScansDown = portExt->PortCapabilities.AdapterScansDown, 368 .CommandQueueing = portExt->PortCapabilities.TaggedQueuing, 369 .AcceleratedTransfer = TRUE, 370 .BusType = BusTypeScsi, // FIXME 371 .BusMajorVersion = 2, 372 .BusMinorVersion = 0 373 }; 374 375 // store returned length 376 Irp->IoStatus.Information = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8); 377 status = STATUS_SUCCESS; 378 379 completeIrp: 380 Irp->IoStatus.Status = status; 381 IoCompleteRequest(Irp, IO_NO_INCREMENT); 382 return status; 383 } 384 385 /********************************************************************** 386 * NAME INTERNAL 387 * ScsiPortDeviceControl 388 * 389 * DESCRIPTION 390 * Answer requests for device control calls 391 * 392 * RUN LEVEL 393 * PASSIVE_LEVEL 394 * 395 * ARGUMENTS 396 * Standard dispatch arguments 397 * 398 * RETURNS 399 * NTSTATUS 400 */ 401 402 NTSTATUS 403 NTAPI 404 ScsiPortDeviceControl( 405 _In_ PDEVICE_OBJECT DeviceObject, 406 _In_ PIRP Irp) 407 { 408 PIO_STACK_LOCATION Stack; 409 PSCSI_PORT_COMMON_EXTENSION comExt = DeviceObject->DeviceExtension; 410 PSCSI_PORT_DEVICE_EXTENSION portExt; 411 PSCSI_PORT_LUN_EXTENSION lunExt; 412 NTSTATUS status; 413 414 DPRINT("ScsiPortDeviceControl()\n"); 415 416 Irp->IoStatus.Information = 0; 417 418 Stack = IoGetCurrentIrpStackLocation(Irp); 419 420 switch (Stack->Parameters.DeviceIoControl.IoControlCode) 421 { 422 case IOCTL_STORAGE_QUERY_PROPERTY: 423 { 424 DPRINT(" IOCTL_STORAGE_QUERY_PROPERTY\n"); 425 426 if (!VerifyIrpInBufferSize(Irp, sizeof(STORAGE_PROPERTY_QUERY))) 427 { 428 status = STATUS_BUFFER_TOO_SMALL; 429 break; 430 } 431 432 if (comExt->IsFDO) 433 return FdoHandleQueryProperty(DeviceObject, Irp); 434 else 435 return PdoHandleQueryProperty(DeviceObject, Irp); 436 } 437 case IOCTL_SCSI_GET_ADDRESS: 438 { 439 DPRINT(" IOCTL_SCSI_GET_ADDRESS\n"); 440 441 if (comExt->IsFDO) 442 { 443 status = STATUS_INVALID_DEVICE_REQUEST; 444 break; 445 } 446 447 PSCSI_ADDRESS address = Irp->AssociatedIrp.SystemBuffer; 448 if (!VerifyIrpOutBufferSize(Irp, sizeof(*address))) 449 { 450 status = STATUS_BUFFER_TOO_SMALL; 451 break; 452 } 453 454 lunExt = DeviceObject->DeviceExtension; 455 portExt = comExt->LowerDevice->DeviceExtension; 456 457 address->Length = sizeof(SCSI_ADDRESS); 458 address->PortNumber = portExt->PortNumber; 459 address->PathId = lunExt->PathId; 460 address->TargetId = lunExt->TargetId; 461 address->Lun = lunExt->Lun; 462 463 Irp->IoStatus.Information = sizeof(SCSI_ADDRESS); 464 status = STATUS_SUCCESS; 465 break; 466 } 467 case IOCTL_SCSI_GET_DUMP_POINTERS: 468 { 469 DPRINT(" IOCTL_SCSI_GET_DUMP_POINTERS\n"); 470 471 if (!comExt->IsFDO) 472 { 473 IoSkipCurrentIrpStackLocation(Irp); 474 return IoCallDriver(comExt->LowerDevice, Irp); 475 } 476 477 PDUMP_POINTERS dumpPointers = Irp->AssociatedIrp.SystemBuffer; 478 if (!VerifyIrpOutBufferSize(Irp, sizeof(*dumpPointers))) 479 { 480 status = STATUS_BUFFER_TOO_SMALL; 481 break; 482 } 483 484 dumpPointers->DeviceObject = DeviceObject; 485 /* More data.. ? */ 486 487 status = STATUS_SUCCESS; 488 Irp->IoStatus.Information = sizeof(DUMP_POINTERS); 489 break; 490 } 491 case IOCTL_SCSI_GET_CAPABILITIES: 492 { 493 DPRINT(" IOCTL_SCSI_GET_CAPABILITIES\n"); 494 495 if (!comExt->IsFDO) 496 { 497 status = STATUS_INVALID_DEVICE_REQUEST; 498 break; 499 } 500 501 if (!VerifyIrpOutBufferSize(Irp, sizeof(IO_SCSI_CAPABILITIES))) 502 { 503 status = STATUS_BUFFER_TOO_SMALL; 504 break; 505 } 506 507 portExt = DeviceObject->DeviceExtension; 508 509 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, 510 &portExt->PortCapabilities, 511 sizeof(IO_SCSI_CAPABILITIES)); 512 513 status = STATUS_SUCCESS; 514 Irp->IoStatus.Information = sizeof(IO_SCSI_CAPABILITIES); 515 break; 516 } 517 case IOCTL_SCSI_GET_INQUIRY_DATA: 518 { 519 DPRINT(" IOCTL_SCSI_GET_INQUIRY_DATA\n"); 520 521 if (!comExt->IsFDO) 522 { 523 status = STATUS_INVALID_DEVICE_REQUEST; 524 break; 525 } 526 527 /* Copy inquiry data to the port device extension */ 528 status = SpiGetInquiryData(DeviceObject->DeviceExtension, Irp); 529 break; 530 } 531 case IOCTL_SCSI_MINIPORT: 532 DPRINT1("IOCTL_SCSI_MINIPORT unimplemented!\n"); 533 status = STATUS_NOT_IMPLEMENTED; 534 break; 535 536 case IOCTL_SCSI_PASS_THROUGH: 537 DPRINT1("IOCTL_SCSI_PASS_THROUGH unimplemented!\n"); 538 status = STATUS_NOT_IMPLEMENTED; 539 break; 540 541 default: 542 DPRINT1("unknown ioctl code: 0x%lX\n", Stack->Parameters.DeviceIoControl.IoControlCode); 543 status = STATUS_NOT_SUPPORTED; 544 break; 545 } 546 547 /* Complete the request with the given status */ 548 Irp->IoStatus.Status = status; 549 IoCompleteRequest(Irp, IO_NO_INCREMENT); 550 551 return status; 552 } 553