xref: /reactos/drivers/storage/port/scsiport/ioctl.c (revision 9393fc32)
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