From b558596409030b3d1a42f8474c7292f938611c1f Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 14 Jul 2025 02:49:51 +0600 Subject: [PATCH] [SPTILIB] Introduce SPTI static library for storage drivers (#8209) Add a SCSI and ATA passthrough support helper library for direct use from low-level storage drivers. Tested with: CDRoller, CloneCD, Magic ISO NOTE: Vbox seems to lack support for CD/DVD burning; tested on real hardware. CORE-10191 CORE-16452 CORE-14788 CORE-18241 CORE-17256 CORE-13866 --- drivers/storage/port/scsiport/CMakeLists.txt | 4 + drivers/storage/port/scsiport/ioctl.c | 73 +- drivers/storage/port/storport/CMakeLists.txt | 4 + drivers/usb/usbstor/CMakeLists.txt | 4 + drivers/usb/usbstor/disk.c | 31 +- sdk/include/ddk/scsi.h | 46 + sdk/include/psdk/ntddscsi.h | 1 + sdk/lib/drivers/CMakeLists.txt | 1 + sdk/lib/drivers/sptilib/CMakeLists.txt | 7 + sdk/lib/drivers/sptilib/sptilib.c | 1008 ++++++++++++++++++ sdk/lib/drivers/sptilib/sptilib.h | 85 ++ sdk/lib/drivers/sptilib/sptilibp.h | 35 + 12 files changed, 1285 insertions(+), 14 deletions(-) create mode 100644 sdk/lib/drivers/sptilib/CMakeLists.txt create mode 100644 sdk/lib/drivers/sptilib/sptilib.c create mode 100644 sdk/lib/drivers/sptilib/sptilib.h create mode 100644 sdk/lib/drivers/sptilib/sptilibp.h diff --git a/drivers/storage/port/scsiport/CMakeLists.txt b/drivers/storage/port/scsiport/CMakeLists.txt index f4bbc0b48e8..8a5fcbdfadc 100644 --- a/drivers/storage/port/scsiport/CMakeLists.txt +++ b/drivers/storage/port/scsiport/CMakeLists.txt @@ -1,6 +1,9 @@ spec2def(scsiport.sys scsiport.spec ADD_IMPORTLIB) +include_directories( + ${REACTOS_SOURCE_DIR}/sdk/lib/drivers/sptilib) + # Embed RTC libs if (STACK_PROTECTOR) target_sources(libscsiport PRIVATE $) @@ -27,5 +30,6 @@ add_library(scsiport MODULE add_pch(scsiport scsiport.h "${PCH_SKIP_SOURCE}") set_module_type(scsiport kernelmodedriver) +target_link_libraries(scsiport sptilib ${PSEH_LIB}) add_importlibs(scsiport ntoskrnl hal) add_cd_file(TARGET scsiport DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/drivers/storage/port/scsiport/ioctl.c b/drivers/storage/port/scsiport/ioctl.c index 8d4c0faab89..15226e78c42 100644 --- a/drivers/storage/port/scsiport/ioctl.c +++ b/drivers/storage/port/scsiport/ioctl.c @@ -8,6 +8,7 @@ */ #include "scsiport.h" +#include #define NDEBUG #include @@ -276,7 +277,6 @@ PdoHandleQueryProperty( } default: { - UNREACHABLE; status = STATUS_NOT_IMPLEMENTED; goto completeIrp; } @@ -288,6 +288,56 @@ completeIrp: return status; } +static +NTSTATUS +PdoHandleScsiPassthrough( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp) +{ + PSCSI_PORT_LUN_EXTENSION lunExt = DeviceObject->DeviceExtension; + PSCSI_PORT_DEVICE_EXTENSION portExt; + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + ASSERT(!lunExt->Common.IsFDO); + + /* Skip requests that bypassed the class driver. See also cdrom!RequestHandleScsiPassThrough */ + if ((IoGetCurrentIrpStackLocation(Irp)->MinorFunction == 0) && lunExt->DeviceClaimed) + return STATUS_INVALID_DEVICE_REQUEST; + + portExt = lunExt->Common.LowerDevice->DeviceExtension; + + return SptiHandleScsiPassthru(DeviceObject, + Irp, + portExt->PortCapabilities.MaximumTransferLength, + portExt->PortCapabilities.MaximumPhysicalPages); +} + +static +NTSTATUS +FdoHandleScsiPassthrough( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp) +{ + PSCSI_PORT_DEVICE_EXTENSION portExt; + PSCSI_PORT_LUN_EXTENSION lunExt; + PSCSI_PASS_THROUGH spt; + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + + if (!VerifyIrpInBufferSize(Irp, RTL_SIZEOF_THROUGH_FIELD(SCSI_PASS_THROUGH, Lun))) + return STATUS_BUFFER_TOO_SMALL; + + portExt = DeviceObject->DeviceExtension; + ASSERT(portExt->Common.IsFDO); + + spt = Irp->AssociatedIrp.SystemBuffer; + lunExt = GetLunByPath(portExt, spt->PathId, spt->TargetId, spt->Lun); + if (!lunExt) + return STATUS_NO_SUCH_DEVICE; + + return PdoHandleScsiPassthrough(lunExt->Common.DeviceObject, Irp); +} + static NTSTATUS FdoHandleQueryProperty( @@ -409,6 +459,7 @@ ScsiPortDeviceControl( PSCSI_PORT_COMMON_EXTENSION comExt = DeviceObject->DeviceExtension; PSCSI_PORT_DEVICE_EXTENSION portExt; PSCSI_PORT_LUN_EXTENSION lunExt; + ULONG IoControlCode; NTSTATUS status; DPRINT("ScsiPortDeviceControl()\n"); @@ -416,8 +467,9 @@ ScsiPortDeviceControl( Irp->IoStatus.Information = 0; Stack = IoGetCurrentIrpStackLocation(Irp); + IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode; - switch (Stack->Parameters.DeviceIoControl.IoControlCode) + switch (IoControlCode) { case IOCTL_STORAGE_QUERY_PROPERTY: { @@ -534,20 +586,27 @@ ScsiPortDeviceControl( break; case IOCTL_SCSI_PASS_THROUGH: - DPRINT1("IOCTL_SCSI_PASS_THROUGH unimplemented!\n"); - status = STATUS_NOT_IMPLEMENTED; + case IOCTL_SCSI_PASS_THROUGH_DIRECT: + { + DPRINT(" IOCTL_SCSI_PASS_THROUGH%s\n", + IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT ? "_DIRECT" : ""); + + if (comExt->IsFDO) + status = FdoHandleScsiPassthrough(DeviceObject, Irp); + else + status = PdoHandleScsiPassthrough(DeviceObject, Irp); break; + } case IOCTL_ATA_PASS_THROUGH: case IOCTL_ATA_PASS_THROUGH_DIRECT: /* ATA passthrough IOCTLs not supported by MS scsiport */ - DPRINT1("ATA passthrough IOCTLs not supported: 0x%lX\n", - Stack->Parameters.DeviceIoControl.IoControlCode); + DPRINT1("ATA passthrough IOCTLs not supported: 0x%lX\n", IoControlCode); status = STATUS_NOT_SUPPORTED; break; default: - DPRINT1("unknown ioctl code: 0x%lX\n", Stack->Parameters.DeviceIoControl.IoControlCode); + DPRINT1("unknown ioctl code: 0x%lX\n", IoControlCode); status = STATUS_NOT_SUPPORTED; break; } diff --git a/drivers/storage/port/storport/CMakeLists.txt b/drivers/storage/port/storport/CMakeLists.txt index 67a35dc52b8..d247bb1bf1b 100644 --- a/drivers/storage/port/storport/CMakeLists.txt +++ b/drivers/storage/port/storport/CMakeLists.txt @@ -1,6 +1,9 @@ spec2def(storport.sys storport.spec ADD_IMPORTLIB) +include_directories( + ${REACTOS_SOURCE_DIR}/sdk/lib/drivers/sptilib) + list(APPEND SOURCE fdo.c miniport.c @@ -20,5 +23,6 @@ add_library(storport MODULE add_pch(storport precomp.h "${PCH_SKIP_SOURCE}") set_module_type(storport kernelmodedriver) +target_link_libraries(storport sptilib ${PSEH_LIB}) add_importlibs(storport ntoskrnl hal) add_cd_file(TARGET storport DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/drivers/usb/usbstor/CMakeLists.txt b/drivers/usb/usbstor/CMakeLists.txt index 95ee0c18c58..147ab69a237 100644 --- a/drivers/usb/usbstor/CMakeLists.txt +++ b/drivers/usb/usbstor/CMakeLists.txt @@ -1,4 +1,7 @@ +include_directories( + ${REACTOS_SOURCE_DIR}/sdk/lib/drivers/sptilib) + list(APPEND SOURCE descriptor.c disk.c @@ -19,6 +22,7 @@ add_library(usbstor MODULE usbstor.rc) set_module_type(usbstor kernelmodedriver) +target_link_libraries(usbstor sptilib ${PSEH_LIB}) add_importlibs(usbstor ntoskrnl hal usbd) add_pch(usbstor usbstor.h "${PCH_SKIP_SOURCE}") add_cd_file(TARGET usbstor DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/drivers/usb/usbstor/disk.c b/drivers/usb/usbstor/disk.c index 7f82bcbd539..0c2e987590f 100644 --- a/drivers/usb/usbstor/disk.c +++ b/drivers/usb/usbstor/disk.c @@ -9,10 +9,13 @@ */ #include "usbstor.h" +#include #define NDEBUG #include +// See CORE-10515 and CORE-10755 +#define USBSTOR_DEFAULT_MAX_PHYS_PAGES (USBSTOR_DEFAULT_MAX_TRANSFER_LENGTH / PAGE_SIZE + 1) static BOOLEAN @@ -394,7 +397,7 @@ USBSTOR_HandleQueryProperty( .Version = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8), .Size = sizeof(STORAGE_ADAPTER_DESCRIPTOR_WIN8), .MaximumTransferLength = USBSTOR_DEFAULT_MAX_TRANSFER_LENGTH, - .MaximumPhysicalPages = USBSTOR_DEFAULT_MAX_TRANSFER_LENGTH / PAGE_SIZE + 1, // See CORE-10515 and CORE-10755 + .MaximumPhysicalPages = USBSTOR_DEFAULT_MAX_PHYS_PAGES, .BusType = BusTypeUsb, .BusMajorVersion = 2, //FIXME verify .BusMinorVersion = 0 //FIXME @@ -429,13 +432,27 @@ USBSTOR_HandleDeviceControl( Status = USBSTOR_HandleQueryProperty(DeviceObject, Irp); break; case IOCTL_SCSI_PASS_THROUGH: - DPRINT1("USBSTOR_HandleDeviceControl IOCTL_SCSI_PASS_THROUGH NOT implemented\n"); - Status = STATUS_NOT_SUPPORTED; - break; case IOCTL_SCSI_PASS_THROUGH_DIRECT: - DPRINT1("USBSTOR_HandleDeviceControl IOCTL_SCSI_PASS_THROUGH_DIRECT NOT implemented\n"); - Status = STATUS_NOT_SUPPORTED; + { + PDODeviceExtension = DeviceObject->DeviceExtension; + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + ASSERT(PDODeviceExtension); + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + /* Skip requests that bypassed the class driver. See also cdrom!RequestHandleScsiPassThrough */ + if ((IoGetCurrentIrpStackLocation(Irp)->MinorFunction == 0) && PDODeviceExtension->Claimed) + { + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + Status = SptiHandleScsiPassthru(DeviceObject, + Irp, + USBSTOR_DEFAULT_MAX_TRANSFER_LENGTH, + USBSTOR_DEFAULT_MAX_PHYS_PAGES); break; + } case IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER: DPRINT1("USBSTOR_HandleDeviceControl IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER NOT implemented\n"); Status = STATUS_NOT_SUPPORTED; @@ -461,7 +478,7 @@ USBSTOR_HandleDeviceControl( if (Capabilities) { Capabilities->MaximumTransferLength = USBSTOR_DEFAULT_MAX_TRANSFER_LENGTH; - Capabilities->MaximumPhysicalPages = USBSTOR_DEFAULT_MAX_TRANSFER_LENGTH / PAGE_SIZE + 1; // See CORE-10515 and CORE-10755 + Capabilities->MaximumPhysicalPages = USBSTOR_DEFAULT_MAX_PHYS_PAGES; Capabilities->SupportedAsynchronousEvents = 0; Capabilities->AlignmentMask = 0; Capabilities->TaggedQueuing = FALSE; diff --git a/sdk/include/ddk/scsi.h b/sdk/include/ddk/scsi.h index 465880aee76..06be26c7ca7 100644 --- a/sdk/include/ddk/scsi.h +++ b/sdk/include/ddk/scsi.h @@ -1886,6 +1886,27 @@ typedef union _CDB { UCHAR Streaming:1; UCHAR Control; } WRITE12; + struct _ATA_PASSTHROUGH12 { + UCHAR OperationCode; + UCHAR Reserved1:1; + UCHAR Protocol:4; + UCHAR MultipleCount:3; + UCHAR TLength:2; + UCHAR ByteBlock:1; + UCHAR TDir:1; + UCHAR Reserved2:1; + UCHAR CkCond:1; + UCHAR Offline:2; + UCHAR Features; + UCHAR SectorCount; + UCHAR LbaLow; + UCHAR LbaMid; + UCHAR LbaHigh; + UCHAR Device; + UCHAR Command; + UCHAR Reserved3; + UCHAR Control; + } ATA_PASSTHROUGH12; struct _READ16 { UCHAR OperationCode; UCHAR Reserved1:3; @@ -1944,6 +1965,31 @@ typedef union _CDB { UCHAR Reserved2:7; UCHAR Control; } READ_CAPACITY16; + struct _ATA_PASSTHROUGH16 { + UCHAR OperationCode; + UCHAR Extend:1; + UCHAR Protocol:4; + UCHAR MultipleCount:3; + UCHAR TLength:2; + UCHAR ByteBlock:1; + UCHAR TDir:1; + UCHAR Reserved1:1; + UCHAR CkCond:1; + UCHAR Offline:2; + UCHAR Features15_8; + UCHAR Features7_0; + UCHAR SectorCount15_8; + UCHAR SectorCount7_0; + UCHAR LbaLow15_8; + UCHAR LbaLow7_0; + UCHAR LbaMid15_8; + UCHAR LbaMid7_0; + UCHAR LbaHigh15_8; + UCHAR LbaHigh7_0; + UCHAR Device; + UCHAR Command; + UCHAR Control; + } ATA_PASSTHROUGH16; struct _TOKEN_OPERATION { UCHAR OperationCode; UCHAR ServiceAction:5; diff --git a/sdk/include/psdk/ntddscsi.h b/sdk/include/psdk/ntddscsi.h index 0d9001b34fb..e60de9a1429 100644 --- a/sdk/include/psdk/ntddscsi.h +++ b/sdk/include/psdk/ntddscsi.h @@ -256,6 +256,7 @@ typedef struct _SCSI_PASS_THROUGH_DIRECT32_EX #define ATA_FLAGS_DATA_OUT (1 << 2) #define ATA_FLAGS_48BIT_COMMAND (1 << 3) #define ATA_FLAGS_USE_DMA (1 << 4) +#define ATA_FLAGS_NO_MULTIPLE (1 << 5) typedef struct _SCSI_BUS_DATA { UCHAR NumberOfLogicalUnits; diff --git a/sdk/lib/drivers/CMakeLists.txt b/sdk/lib/drivers/CMakeLists.txt index 4e43c7f8bec..8ec5771addc 100644 --- a/sdk/lib/drivers/CMakeLists.txt +++ b/sdk/lib/drivers/CMakeLists.txt @@ -9,5 +9,6 @@ add_subdirectory(rdbsslib) add_subdirectory(rtlver) add_subdirectory(rxce) add_subdirectory(sound) +add_subdirectory(sptilib) add_subdirectory(virtio) add_subdirectory(wdf) diff --git a/sdk/lib/drivers/sptilib/CMakeLists.txt b/sdk/lib/drivers/sptilib/CMakeLists.txt new file mode 100644 index 00000000000..5db46f94076 --- /dev/null +++ b/sdk/lib/drivers/sptilib/CMakeLists.txt @@ -0,0 +1,7 @@ + +list(APPEND SOURCE + sptilib.c) + +add_library(sptilib ${SOURCE}) +target_link_libraries(sptilib PRIVATE ${PSEH_LIB}) +add_dependencies(sptilib xdk) diff --git a/sdk/lib/drivers/sptilib/sptilib.c b/sdk/lib/drivers/sptilib/sptilib.c new file mode 100644 index 00000000000..444aae90094 --- /dev/null +++ b/sdk/lib/drivers/sptilib/sptilib.c @@ -0,0 +1,1008 @@ +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: ATA and SCSI Pass Through Interface for storage drivers + * COPYRIGHT: Copyright 2025 Dmitry Borisov + */ + +/* + * This library provides helper code for handling ATA and SCSI Pass Through IOCTLs. + * Typically, these IRPs come from user-mode applications. + * The handler will translate the IOCTL into an SRB and send it to the PDO. + */ + +/* INCLUDES *******************************************************************/ + +#include +#include +#include +#include +#include + +#define NDEBUG +#include + +#include "sptilib.h" +#include "sptilibp.h" + +/* PRIVATE FUNCTIONS **********************************************************/ + +_At_(IrpContext->Srb.SenseInfoBuffer, __drv_freesMem(Mem)) +static +CODE_SEG("PAGE") +VOID +SptiFreeIrpContext( + _In_opt_ __drv_freesMem(Mem) PPASSTHROUGH_IRP_CONTEXT IrpContext) +{ + PIRP Irp; + + PAGED_CODE(); + + ASSERT(IrpContext); + + Irp = IrpContext->Irp; + if (Irp) + { + if (Irp->MdlAddress) + { + MmUnlockPages(Irp->MdlAddress); + IoFreeMdl(Irp->MdlAddress); + } + Irp->MdlAddress = NULL; + IoFreeIrp(Irp); + } + + if (IrpContext->Srb.SenseInfoBuffer) + ExFreePoolWithTag(IrpContext->Srb.SenseInfoBuffer, TAG_SPTI); + + ExFreePoolWithTag(IrpContext, TAG_SPTI); +} + +static IO_COMPLETION_ROUTINE SptiCompletionRoutine; +static +NTSTATUS +NTAPI +SptiCompletionRoutine( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Context) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + if (Irp->PendingReturned) + KeSetEvent(Context, IO_NO_INCREMENT, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +__drv_allocatesMem(Mem) +static +CODE_SEG("PAGE") +PPASSTHROUGH_IRP_CONTEXT +SptiCreateIrpContext( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP OriginalIrp, + _In_ PVOID DataBuffer, + _In_ ULONG DataBufferLength, + _In_ BOOLEAN IsDirectMemoryAccess, + _In_ BOOLEAN IsBufferReadAccess) +{ + PPASSTHROUGH_IRP_CONTEXT IrpContext; + PIRP Irp; + PIO_STACK_LOCATION IoStack; + + PAGED_CODE(); + + IrpContext = ExAllocatePoolZero(NonPagedPool, sizeof(*IrpContext), TAG_SPTI); + if (!IrpContext) + return NULL; + + IrpContext->Irp = Irp = IoAllocateIrp(DeviceObject->StackSize, 0); + if (!Irp) + goto Cleanup; + + Irp->Tail.Overlay.Thread = PsGetCurrentThread(); + + if (DataBuffer) + { + if (!IoAllocateMdl(DataBuffer, DataBufferLength, FALSE, FALSE, Irp)) + goto Cleanup; + ASSERT(Irp->MdlAddress); + + _SEH2_TRY + { + MmProbeAndLockPages(Irp->MdlAddress, + IsDirectMemoryAccess ? OriginalIrp->RequestorMode : KernelMode, + IsBufferReadAccess ? IoReadAccess : IoWriteAccess); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(goto Cleanup); + } + _SEH2_END; + } + + IrpContext->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + IrpContext->Srb.Length = RTL_FIELD_SIZE(PASSTHROUGH_IRP_CONTEXT, Srb); + IrpContext->Srb.OriginalRequest = Irp; + IrpContext->Srb.DataBuffer = DataBuffer; + IrpContext->Srb.DataTransferLength = DataBufferLength; + IrpContext->Srb.SrbFlags = SRB_FLAGS_NO_QUEUE_FREEZE; + + IoStack = IoGetNextIrpStackLocation(Irp); + IoStack->MinorFunction = IRP_MN_SCSI_CLASS; + IoStack->MajorFunction = IRP_MJ_SCSI; + IoStack->Parameters.Scsi.Srb = &IrpContext->Srb; + + return IrpContext; + +Cleanup: + DPRINT1("Failed to create IRP\n"); + SptiFreeIrpContext(IrpContext); + return NULL; +} + +static +CODE_SEG("PAGE") +VOID +SptiInitializeOutputBuffer( + _In_ PIRP Irp) +{ + PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); + ULONG OutputBufferLength = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG InputBufferLength = IoStack->Parameters.DeviceIoControl.InputBufferLength; + + PAGED_CODE(); + + if (OutputBufferLength > InputBufferLength) + { + ULONG_PTR BufferStart = (ULONG_PTR)Irp->AssociatedIrp.SystemBuffer + InputBufferLength; + RtlZeroMemory((PVOID)BufferStart, OutputBufferLength - InputBufferLength); + } +} + +static +CODE_SEG("PAGE") +NTSTATUS +SptiCallDriver( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PPASSTHROUGH_IRP_CONTEXT IrpContext) +{ + PIRP Irp = IrpContext->Irp; + KEVENT Event; + NTSTATUS Status; + + PAGED_CODE(); + + SptiInitializeOutputBuffer(Irp); + + // TODO: Send the IRP in an asynchronous way (do not block the user app thread) + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + IoSetCompletionRoutine(Irp, + SptiCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE); + + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + return Status; +} + +_At_(Srb->SenseInfoBuffer, __drv_allocatesMem(Mem)) +static +CODE_SEG("PAGE") +NTSTATUS +SptiSrbAppendSenseBuffer( + _In_ PSCSI_REQUEST_BLOCK Srb, + _In_ ULONG BufferSize) +{ + PAGED_CODE(); + + if (BufferSize == 0) + { + Srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE; + return STATUS_SUCCESS; + } + + Srb->SenseInfoBuffer = ExAllocatePoolUninitialized(NonPagedPoolCacheAligned, + BufferSize, + TAG_SPTI); + if (!Srb->SenseInfoBuffer) + return STATUS_INSUFFICIENT_RESOURCES; + + Srb->SenseInfoBufferLength = BufferSize; + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +VOID +SptiTranslateTaskFileToCdb( + _Out_ CDB* __restrict Cdb, + _In_ UCHAR* __restrict TaskFile, + _In_ USHORT AtaFlags) +{ + UCHAR Protocol; + + PAGED_CODE(); + + Cdb->ATA_PASSTHROUGH16.OperationCode = SCSIOP_ATA_PASSTHROUGH16; + + Cdb->ATA_PASSTHROUGH16.Features7_0 = TaskFile[8 + 0]; + Cdb->ATA_PASSTHROUGH16.SectorCount7_0 = TaskFile[8 + 1]; + Cdb->ATA_PASSTHROUGH16.LbaLow7_0 = TaskFile[8 + 2]; + Cdb->ATA_PASSTHROUGH16.LbaMid7_0 = TaskFile[8 + 3]; + Cdb->ATA_PASSTHROUGH16.LbaHigh7_0 = TaskFile[8 + 4]; + Cdb->ATA_PASSTHROUGH16.Device = TaskFile[8 + 5]; + Cdb->ATA_PASSTHROUGH16.Command = TaskFile[8 + 6]; + + if (AtaFlags & ATA_FLAGS_48BIT_COMMAND) + { + Cdb->ATA_PASSTHROUGH16.Features15_8 = TaskFile[0]; + Cdb->ATA_PASSTHROUGH16.SectorCount15_8 = TaskFile[1]; + Cdb->ATA_PASSTHROUGH16.LbaLow15_8 = TaskFile[2]; + Cdb->ATA_PASSTHROUGH16.LbaMid15_8 = TaskFile[3]; + Cdb->ATA_PASSTHROUGH16.LbaHigh15_8 = TaskFile[4]; + + Cdb->ATA_PASSTHROUGH16.Extend = 1; + } + + /* Enable the check condition to get ATA fields from the device */ + Cdb->ATA_PASSTHROUGH16.CkCond = 1; + + if (AtaFlags & (ATA_FLAGS_DATA_IN | ATA_FLAGS_DATA_OUT)) + { + Cdb->ATA_PASSTHROUGH16.TLength = 3; + + if (AtaFlags & ATA_FLAGS_DATA_IN) + Cdb->ATA_PASSTHROUGH16.TDir = 1; + } + + if (AtaFlags & ATA_FLAGS_USE_DMA) + { + Protocol = ATA_PASSTHROUGH_PROTOCOL_DMA; + } + else + { + if (AtaFlags & (ATA_FLAGS_DATA_IN | ATA_FLAGS_DATA_OUT)) + { + if (AtaFlags & ATA_FLAGS_DATA_IN) + Protocol = ATA_PASSTHROUGH_PROTOCOL_PIO_DATA_IN; + else + Protocol = ATA_PASSTHROUGH_PROTOCOL_PIO_DATA_OUT; + } + else + { + Protocol = ATA_PASSTHROUGH_PROTOCOL_NON_DATA; + } + } + Cdb->ATA_PASSTHROUGH16.Protocol = Protocol; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SptiTranslateAtaStatusReturnToTaskFile( + _In_ SCSI_REQUEST_BLOCK* __restrict Srb, + _In_ DESCRIPTOR_SENSE_DATA* __restrict SenseData, + _Out_ UCHAR* __restrict TaskFile) +{ + BOOLEAN WasCheckCondition = FALSE; + + PAGED_CODE(); + + if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) + return WasCheckCondition; + + if (SenseData->ErrorCode == SCSI_SENSE_ERRORCODE_DESCRIPTOR_CURRENT) + { + PSCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN Descriptor; + + Descriptor = (PSCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN)&SenseData->DescriptorBuffer[0]; + + if (Descriptor->Header.DescriptorType == SCSI_SENSE_DESCRIPTOR_TYPE_ATA_STATUS_RETURN) + { + TaskFile[8 + 0] = Descriptor->Error; + TaskFile[8 + 1] = Descriptor->SectorCount7_0; + TaskFile[8 + 2] = Descriptor->LbaLow7_0; + TaskFile[8 + 3] = Descriptor->LbaMid7_0; + TaskFile[8 + 4] = Descriptor->LbaHigh7_0; + TaskFile[8 + 5] = Descriptor->Device; + TaskFile[8 + 6] = Descriptor->Status; + + if (Descriptor->Extend) + { + TaskFile[1] = Descriptor->SectorCount15_8; + TaskFile[2] = Descriptor->LbaLow15_8; + TaskFile[3] = Descriptor->LbaMid15_8; + TaskFile[4] = Descriptor->LbaHigh15_8; + } + } + } + + /* + * The check condition bit will cause the APT command to return an error upon completion. + * We should determine if there was a real error or not. + */ + if ((Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (SenseData->SenseKey == SCSI_SENSE_RECOVERED_ERROR) && + (SenseData->AdditionalSenseCode == SCSI_ADSENSE_NO_SENSE) && + (SenseData->AdditionalSenseCodeQualifier == + SCSI_SENSEQ_ATA_PASS_THROUGH_INFORMATION_AVAILABLE)) + { + WasCheckCondition = TRUE; + } + + return WasCheckCondition; +} + +static +CODE_SEG("PAGE") +NTSTATUS +SptiTranslateAptToSrb( + _Out_ SCSI_REQUEST_BLOCK* __restrict Srb, + _In_ ATA_PASS_THROUGH_EX* __restrict Apt, + _In_ PUCHAR TaskFile) +{ + NTSTATUS Status; + + PAGED_CODE(); + + Status = SptiSrbAppendSenseBuffer(Srb, + FIELD_OFFSET(DESCRIPTOR_SENSE_DATA, DescriptorBuffer) + + sizeof(SCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN)); + if (!NT_SUCCESS(Status)) + return Status; + + Srb->CdbLength = RTL_FIELD_SIZE(CDB, ATA_PASSTHROUGH16); + Srb->TimeOutValue = Apt->TimeOutValue; + + Srb->PathId = Apt->PathId; + Srb->TargetId = Apt->TargetId; + Srb->Lun = Apt->Lun; + + if (Apt->AtaFlags & ATA_FLAGS_DATA_IN) + Srb->SrbFlags |= SRB_FLAGS_DATA_IN; + if (Apt->AtaFlags & ATA_FLAGS_DATA_OUT) + Srb->SrbFlags |= SRB_FLAGS_DATA_OUT; + + SptiTranslateTaskFileToCdb((PCDB)Srb->Cdb, TaskFile, Apt->AtaFlags); + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +SptiInitializeApt( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PIO_STACK_LOCATION IoStack, + _In_ BOOLEAN IsDirectMemoryAccess, + _In_ ULONG MaximumTransferLength, + _In_ ULONG MaximumPhysicalPages, + _In_ PATA_PASS_THROUGH_EX Apt, + _Out_ PPASSTHROUGH_DATA AptData, + _Out_ PUCHAR* TaskFile, + _Out_ PVOID* DataBuffer) +{ + ULONG StructSize; + BOOLEAN IsNativeStructSize; + + PAGED_CODE(); + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + if (IoIs32bitProcess(Irp)) + { + StructSize = sizeof(ATA_PASS_THROUGH_EX32); + *TaskFile = (PVOID)((ULONG_PTR)Apt + FIELD_OFFSET(ATA_PASS_THROUGH_EX32, PreviousTaskFile)); + IsNativeStructSize = FALSE; + } + else +#endif + { + StructSize = sizeof(ATA_PASS_THROUGH_EX); + *TaskFile = (PVOID)((ULONG_PTR)Apt + FIELD_OFFSET(ATA_PASS_THROUGH_EX, PreviousTaskFile)); + IsNativeStructSize = TRUE; + } + + if (IoStack->Parameters.DeviceIoControl.InputBufferLength < StructSize) + { + DPRINT1("Buffer too small %lu/%lu\n", + IoStack->Parameters.DeviceIoControl.InputBufferLength, StructSize); + return STATUS_BUFFER_TOO_SMALL; + } + + if (Apt->Length != StructSize) + { + DPRINT1("Unknown structure size %lu\n", StructSize); + return STATUS_REVISION_MISMATCH; + } + + if (Apt->DataTransferLength > MaximumTransferLength) + { + DPRINT1("Too large transfer %lu/%lu\n", Apt->DataTransferLength, MaximumTransferLength); + return STATUS_INVALID_PARAMETER; + } + + if (BYTES_TO_PAGES(Apt->DataTransferLength) > MaximumPhysicalPages) + { + DPRINT1("Too large transfer %lu/%lu pages\n", + BYTES_TO_PAGES(Apt->DataTransferLength), MaximumPhysicalPages); + return STATUS_INVALID_PARAMETER; + } + + /* Retrieve the data buffer or the data buffer offset */ +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + AptData->Buffer = NULL; + RtlCopyMemory(AptData, + (PVOID)((ULONG_PTR)Apt + FIELD_OFFSET(ATA_PASS_THROUGH_EX, DataBufferOffset)), + IsNativeStructSize ? sizeof(PVOID) : sizeof(ULONG32)); +#else + DBG_UNREFERENCED_LOCAL_VARIABLE(IsNativeStructSize); + + AptData->BufferOffset = Apt->DataBufferOffset; +#endif + + if (Apt->DataTransferLength != 0) + { + if (IsDirectMemoryAccess) + *DataBuffer = AptData->Buffer; + else + *DataBuffer = (PVOID)((ULONG_PTR)Apt + AptData->BufferOffset); + } + else + { + *DataBuffer = NULL; + } + + // FIXME: Validate the command opcode + + if (*(PULONG_PTR)DataBuffer & DeviceObject->AlignmentRequirement) + { + DPRINT1("Unaligned data buffer %p:%lx\n", + (PVOID)*(PULONG_PTR)DataBuffer, DeviceObject->AlignmentRequirement); + return STATUS_INVALID_PARAMETER; + } + + if (Apt->DataTransferLength & DeviceObject->AlignmentRequirement) + { + DPRINT1("Unaligned data transfer length %lx:%lx\n", + Apt->DataTransferLength, DeviceObject->AlignmentRequirement); + return STATUS_INVALID_PARAMETER; + } + + if ((Apt->AtaFlags & (ATA_FLAGS_DATA_IN | ATA_FLAGS_DATA_OUT)) && + (Apt->DataTransferLength == 0)) + { + DPRINT1("Data buffer too small\n"); + return STATUS_BUFFER_TOO_SMALL; + } + + if (Apt->TimeOutValue < PASSTHROUGH_CMD_TIMEOUT_MIN_SEC || + Apt->TimeOutValue > PASSTHROUGH_CMD_TIMEOUT_MAX_SEC) + { + DPRINT1("Invalid timeout value %lu\n", Apt->TimeOutValue); + return STATUS_INVALID_PARAMETER; + } + + if (!IsDirectMemoryAccess) + { + ULONG_PTR DataBufferEnd; + NTSTATUS Status; + + Status = RtlULongPtrAdd(AptData->BufferOffset, Apt->DataTransferLength, &DataBufferEnd); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Invalid data buffer offset\n"); + return Status; + } + + if ((Apt->Length > AptData->BufferOffset) && (Apt->DataTransferLength != 0)) + { + DPRINT1("Data buffer overlaps APT\n"); + return STATUS_INVALID_PARAMETER; + } + + if ((Apt->AtaFlags & ATA_FLAGS_DATA_IN) && + (DataBufferEnd > IoStack->Parameters.DeviceIoControl.OutputBufferLength)) + { + DPRINT1("Data buffer outside of available space\n"); + return STATUS_INVALID_PARAMETER; + } + + if ((Apt->AtaFlags & ATA_FLAGS_DATA_OUT) && + (DataBufferEnd > IoStack->Parameters.DeviceIoControl.InputBufferLength)) + { + DPRINT1("Data buffer outside of available space\n"); + return STATUS_INVALID_PARAMETER; + } + } + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +SptiTranslateSptToSrb( + _Out_ SCSI_REQUEST_BLOCK* __restrict Srb, + _In_ SCSI_PASS_THROUGH* __restrict Spt, + _In_ PUCHAR Cdb) +{ + NTSTATUS Status; + + PAGED_CODE(); + + Status = SptiSrbAppendSenseBuffer(Srb, Spt->SenseInfoLength); + if (!NT_SUCCESS(Status)) + return Status; + + Srb->CdbLength = Spt->CdbLength; + Srb->TimeOutValue = Spt->TimeOutValue; + + Srb->PathId = Spt->PathId; + Srb->TargetId = Spt->TargetId; + Srb->Lun = Spt->Lun; + + if (Spt->DataTransferLength != 0) + { + switch (Spt->DataIn) + { + case SCSI_IOCTL_DATA_IN: + Srb->SrbFlags |= SRB_FLAGS_DATA_IN; + break; + case SCSI_IOCTL_DATA_OUT: + Srb->SrbFlags |= SRB_FLAGS_DATA_OUT; + break; + default: // SCSI_IOCTL_DATA_UNSPECIFIED + Srb->SrbFlags |= SRB_FLAGS_UNSPECIFIED_DIRECTION; + break; + } + } + + RtlCopyMemory(Srb->Cdb, Cdb, Srb->CdbLength); + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +SptiInitializeSpt( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PIO_STACK_LOCATION IoStack, + _In_ BOOLEAN IsDirectMemoryAccess, + _In_ ULONG MaximumTransferLength, + _In_ ULONG MaximumPhysicalPages, + _In_ PSCSI_PASS_THROUGH Spt, + _Out_ PPASSTHROUGH_DATA SptData, + _Out_ PULONG SenseInfoOffset, + _Out_ PUCHAR* Cdb, + _Out_ PVOID* DataBuffer) +{ + ULONG StructSize; + BOOLEAN IsNativeStructSize; + PULONG SenseInfoOffsetPtr; + + PAGED_CODE(); + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + if (IoIs32bitProcess(Irp)) + { + StructSize = sizeof(SCSI_PASS_THROUGH32); + SenseInfoOffsetPtr = (PVOID)((ULONG_PTR)Spt + + FIELD_OFFSET(SCSI_PASS_THROUGH32, SenseInfoOffset)); + *Cdb = (PVOID)((ULONG_PTR)Spt + FIELD_OFFSET(SCSI_PASS_THROUGH32, Cdb)); + IsNativeStructSize = FALSE; + } + else +#endif + { + StructSize = sizeof(SCSI_PASS_THROUGH); + SenseInfoOffsetPtr = (PVOID)((ULONG_PTR)Spt + + FIELD_OFFSET(SCSI_PASS_THROUGH, SenseInfoOffset)); + *Cdb = (PVOID)((ULONG_PTR)Spt + FIELD_OFFSET(SCSI_PASS_THROUGH, Cdb)); + IsNativeStructSize = TRUE; + } + + if (IoStack->Parameters.DeviceIoControl.InputBufferLength < StructSize) + { + DPRINT1("Buffer too small %lu/%lu\n", + IoStack->Parameters.DeviceIoControl.InputBufferLength, StructSize); + return STATUS_BUFFER_TOO_SMALL; + } + + if (Spt->Length != StructSize) + { + DPRINT1("Unknown structure size %lu\n", StructSize); + return STATUS_REVISION_MISMATCH; + } + + if (Spt->DataTransferLength > MaximumTransferLength) + { + DPRINT1("Too large transfer %lu/%lu\n", Spt->DataTransferLength, MaximumTransferLength); + return STATUS_INVALID_PARAMETER; + } + + if (BYTES_TO_PAGES(Spt->DataTransferLength) > MaximumPhysicalPages) + { + DPRINT1("Too large transfer %lu/%lu pages\n", + BYTES_TO_PAGES(Spt->DataTransferLength), MaximumPhysicalPages); + return STATUS_INVALID_PARAMETER; + } + + /* Retrieve the data buffer or the data buffer offset */ +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + SptData->Buffer = NULL; + RtlCopyMemory(SptData, + (PVOID)((ULONG_PTR)Spt + FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)), + IsNativeStructSize ? sizeof(PVOID) : sizeof(ULONG32)); +#else + DBG_UNREFERENCED_LOCAL_VARIABLE(IsNativeStructSize); + + SptData->BufferOffset = Spt->DataBufferOffset; +#endif + *SenseInfoOffset = *SenseInfoOffsetPtr; + + if (Spt->DataTransferLength != 0) + { + if (IsDirectMemoryAccess) + *DataBuffer = SptData->Buffer; + else + *DataBuffer = (PVOID)((ULONG_PTR)Spt + SptData->BufferOffset); + } + else + { + *DataBuffer = NULL; + } + + // FIXME: Validate the command opcode + + if (*(PULONG_PTR)DataBuffer & DeviceObject->AlignmentRequirement) + { + DPRINT1("Unaligned data buffer %p:%lx\n", + (PVOID)*(PULONG_PTR)DataBuffer, DeviceObject->AlignmentRequirement); + return STATUS_INVALID_PARAMETER; + } + + if (Spt->DataTransferLength & DeviceObject->AlignmentRequirement) + { + DPRINT1("Unaligned data transfer length %lx:%lx\n", + Spt->DataTransferLength, DeviceObject->AlignmentRequirement); + return STATUS_INVALID_PARAMETER; + } + + if (Spt->TimeOutValue < PASSTHROUGH_CMD_TIMEOUT_MIN_SEC || + Spt->TimeOutValue > PASSTHROUGH_CMD_TIMEOUT_MAX_SEC) + { + DPRINT1("Invalid timeout value %lu\n", Spt->TimeOutValue); + return STATUS_INVALID_PARAMETER; + } + + if (Spt->CdbLength > RTL_FIELD_SIZE(SCSI_REQUEST_BLOCK, Cdb)) + { + DPRINT1("Invalid CDB size %u\n", Spt->CdbLength); + return STATUS_INVALID_PARAMETER; + } + + if (Spt->DataIn > SCSI_IOCTL_DATA_UNSPECIFIED) + { + DPRINT1("Unknown DataIn value %u\n", Spt->DataIn); + return STATUS_INVALID_PARAMETER; + } + + if (Spt->SenseInfoLength != 0) + { + ULONG SenseBufferEnd; + NTSTATUS Status; + + Status = RtlULongAdd(*SenseInfoOffset, Spt->SenseInfoLength, &SenseBufferEnd); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Invalid sense buffer offset\n"); + return Status; + } + + if (Spt->Length > *SenseInfoOffset) + { + DPRINT1("Sense buffer overlaps SPT\n"); + return STATUS_INVALID_PARAMETER; + } + + if (!IsDirectMemoryAccess) + { + if ((SenseBufferEnd > SptData->BufferOffset) && (Spt->DataTransferLength != 0)) + { + DPRINT1("Sense buffer overlaps data buffer\n"); + return STATUS_INVALID_PARAMETER; + } + + if (SenseBufferEnd > IoStack->Parameters.DeviceIoControl.OutputBufferLength) + { + DPRINT1("Sense buffer outside of available space\n"); + return STATUS_INVALID_PARAMETER; + } + } + } + + if (!IsDirectMemoryAccess) + { + ULONG_PTR DataBufferEnd; + NTSTATUS Status; + + Status = RtlULongPtrAdd(SptData->BufferOffset, Spt->DataTransferLength, &DataBufferEnd); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Invalid data buffer offset\n"); + return Status; + } + + if ((Spt->Length > SptData->BufferOffset) && (Spt->DataTransferLength != 0)) + { + DPRINT1("Data buffer inside of structure bounds\n"); + return STATUS_INVALID_PARAMETER; + } + + if (Spt->DataIn == SCSI_IOCTL_DATA_IN || Spt->DataIn == SCSI_IOCTL_DATA_UNSPECIFIED) + { + if (DataBufferEnd > IoStack->Parameters.DeviceIoControl.OutputBufferLength) + { + DPRINT1("Data buffer outside of available space\n"); + return STATUS_INVALID_PARAMETER; + } + } + + if (Spt->DataIn == SCSI_IOCTL_DATA_OUT || Spt->DataIn == SCSI_IOCTL_DATA_UNSPECIFIED) + { + if (DataBufferEnd > IoStack->Parameters.DeviceIoControl.InputBufferLength) + { + DPRINT1("Data buffer outside of available space\n"); + return STATUS_INVALID_PARAMETER; + } + } + } + + return STATUS_SUCCESS; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +CODE_SEG("PAGE") +NTSTATUS +SptiHandleAtaPassthru( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp, + _In_ ULONG MaximumTransferLength, + _In_ ULONG MaximumPhysicalPages) +{ + PATA_PASS_THROUGH_EX Apt = Irp->AssociatedIrp.SystemBuffer; + PIO_STACK_LOCATION IoStack; + BOOLEAN IsDirectMemoryAccess; + PASSTHROUGH_DATA AptData; + PUCHAR TaskFile; + NTSTATUS Status; + PVOID DataBuffer; + PPASSTHROUGH_IRP_CONTEXT IrpContext; + PSCSI_REQUEST_BLOCK Srb; + + PAGED_CODE(); + + IoStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(GET_IOCTL(IoStack) == IOCTL_ATA_PASS_THROUGH || + GET_IOCTL(IoStack) == IOCTL_ATA_PASS_THROUGH_DIRECT); + + IsDirectMemoryAccess = (GET_IOCTL(IoStack) == IOCTL_ATA_PASS_THROUGH_DIRECT); + + Status = SptiInitializeApt(DeviceObject, + Irp, + IoStack, + IsDirectMemoryAccess, + MaximumTransferLength, + MaximumPhysicalPages, + Apt, + &AptData, + &TaskFile, + &DataBuffer); + if (!NT_SUCCESS(Status)) + return Status; + + DPRINT("APT: Flags %x DTL %lu\n", Apt->AtaFlags, Apt->DataTransferLength); + DPRINT("APT: Curr %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + TaskFile[8 + 0], TaskFile[8 + 1], TaskFile[8 + 2], TaskFile[8 + 3], + TaskFile[8 + 4], TaskFile[8 + 5], TaskFile[8 + 6], + Apt->AtaFlags, + Apt->DataTransferLength); + if (Apt->AtaFlags & ATA_FLAGS_48BIT_COMMAND) + { + DPRINT("APT: Prev %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + TaskFile[0], TaskFile[1], TaskFile[2], TaskFile[3], + TaskFile[4], TaskFile[5], TaskFile[6]); + } + + IrpContext = SptiCreateIrpContext(DeviceObject, + Irp, + DataBuffer, + Apt->DataTransferLength, + IsDirectMemoryAccess, + !!(Apt->AtaFlags & ATA_FLAGS_DATA_OUT)); + if (!IrpContext) + return STATUS_INSUFFICIENT_RESOURCES; + + Status = SptiTranslateAptToSrb(&IrpContext->Srb, Apt, TaskFile); + if (!NT_SUCCESS(Status)) + goto Cleanup; + + Status = SptiCallDriver(DeviceObject, IrpContext); + + Srb = &IrpContext->Srb; + + if (SptiTranslateAtaStatusReturnToTaskFile(Srb, Srb->SenseInfoBuffer, TaskFile)) + Status = STATUS_SUCCESS; + + /* Update the output buffer size */ + Apt->DataTransferLength = Srb->DataTransferLength; + if (IsDirectMemoryAccess) + { + Irp->IoStatus.Information = Apt->Length; + } + else + { + if ((Apt->AtaFlags & ATA_FLAGS_DATA_IN) && (AptData.BufferOffset != 0)) + Irp->IoStatus.Information = AptData.BufferOffset + Apt->DataTransferLength; + else + Irp->IoStatus.Information = Apt->Length; + } + + DPRINT("APT: Return %lx, DTL %lu, %lu bytes\n", + Status, + Apt->DataTransferLength, + (ULONG)Irp->IoStatus.Information); + DPRINT("APT: Return curr %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + TaskFile[8 + 0], TaskFile[8 + 1], TaskFile[8 + 2], TaskFile[8 + 3], + TaskFile[8 + 4], TaskFile[8 + 5], TaskFile[8 + 6]); + if (Apt->AtaFlags & ATA_FLAGS_48BIT_COMMAND) + { + DPRINT("APT: Return prev %02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + TaskFile[0], TaskFile[1], TaskFile[2], TaskFile[3], + TaskFile[4], TaskFile[5], TaskFile[6]); + } + +Cleanup: + SptiFreeIrpContext(IrpContext); + return Status; +} + +CODE_SEG("PAGE") +NTSTATUS +SptiHandleScsiPassthru( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp, + _In_ ULONG MaximumTransferLength, + _In_ ULONG MaximumPhysicalPages) +{ + PSCSI_PASS_THROUGH Spt = Irp->AssociatedIrp.SystemBuffer; + PIO_STACK_LOCATION IoStack; + BOOLEAN IsDirectMemoryAccess; + PASSTHROUGH_DATA SptData; + ULONG SenseInfoOffset; + PUCHAR Cdb; + NTSTATUS Status; + PVOID DataBuffer; + PPASSTHROUGH_IRP_CONTEXT IrpContext; + PSCSI_REQUEST_BLOCK Srb; + + PAGED_CODE(); + + IoStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(GET_IOCTL(IoStack) == IOCTL_SCSI_PASS_THROUGH || + GET_IOCTL(IoStack) == IOCTL_SCSI_PASS_THROUGH_DIRECT); + + IsDirectMemoryAccess = (GET_IOCTL(IoStack) == IOCTL_SCSI_PASS_THROUGH_DIRECT); + + Status = SptiInitializeSpt(DeviceObject, + Irp, + IoStack, + IsDirectMemoryAccess, + MaximumTransferLength, + MaximumPhysicalPages, + Spt, + &SptData, + &SenseInfoOffset, + &Cdb, + &DataBuffer); + if (!NT_SUCCESS(Status)) + return Status; + + DPRINT("SPT: Flags %u, DTL %lu, sense len %u\n", + Spt->DataIn, + Spt->DataTransferLength, + Spt->SenseInfoLength); + DPRINT("SPT: CDB %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + Cdb[0], Cdb[1], Cdb[2], Cdb[3], Cdb[4], Cdb[5], Cdb[6], Cdb[7], Cdb[8], Cdb[9]); + + IrpContext = SptiCreateIrpContext(DeviceObject, + Irp, + DataBuffer, + Spt->DataTransferLength, + IsDirectMemoryAccess, + !!(Spt->DataIn == SCSI_IOCTL_DATA_OUT)); + if (!IrpContext) + return STATUS_INSUFFICIENT_RESOURCES; + + Status = SptiTranslateSptToSrb(&IrpContext->Srb, Spt, Cdb); + if (!NT_SUCCESS(Status)) + goto Cleanup; + + Status = SptiCallDriver(DeviceObject, IrpContext); + + Srb = &IrpContext->Srb; + + /* + * Low-level storage drivers map SRB_STATUS_DATA_OVERRUN to STATUS_BUFFER_OVERFLOW. + * This SRB status is usually set on an underrun and means success in that case. + */ + if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) + Status = STATUS_SUCCESS; + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) + { + ASSERT(Srb->SenseInfoBufferLength <= Spt->SenseInfoLength); + + Spt->SenseInfoLength = Srb->SenseInfoBufferLength; + + /* Copy the sense buffer back */ + RtlCopyMemory((PVOID)((ULONG_PTR)Spt + SenseInfoOffset), + Srb->SenseInfoBuffer, + Srb->SenseInfoBufferLength); + } + else + { + Spt->SenseInfoLength = 0; + } + + Spt->ScsiStatus = Srb->ScsiStatus; + + /* Update the output buffer size */ + Spt->DataTransferLength = Srb->DataTransferLength; + if (IsDirectMemoryAccess) + { + if (Spt->SenseInfoLength != 0) + Irp->IoStatus.Information = SenseInfoOffset + Spt->SenseInfoLength; + else + Irp->IoStatus.Information = Spt->Length; + } + else + { + if ((Srb->SrbFlags & SRB_FLAGS_DATA_IN) && (SptData.BufferOffset != 0)) + Irp->IoStatus.Information = SptData.BufferOffset + Spt->DataTransferLength; + else if (Spt->SenseInfoLength != 0) + Irp->IoStatus.Information = SenseInfoOffset + Spt->SenseInfoLength; + else + Irp->IoStatus.Information = Spt->Length; + } + + DPRINT("SPT: Return %lx, DTL %lu, " + "Srb status %02x, SCSI status %02x, sense len %u, %lu bytes\n", + Status, + Spt->DataTransferLength, + Srb->SrbStatus, + Spt->ScsiStatus, + Spt->SenseInfoLength, + (ULONG)Irp->IoStatus.Information); + +Cleanup: + SptiFreeIrpContext(IrpContext); + return Status; +} diff --git a/sdk/lib/drivers/sptilib/sptilib.h b/sdk/lib/drivers/sptilib/sptilib.h new file mode 100644 index 00000000000..9aa277751cc --- /dev/null +++ b/sdk/lib/drivers/sptilib/sptilib.h @@ -0,0 +1,85 @@ +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Public header of the ATA and SCSI Pass Through Interface for storage drivers + * COPYRIGHT: Copyright 2025 Dmitry Borisov + */ + +#pragma once + +/** + * ATA Protocol field definitions for the ATA passthrough commands + * (SCSIOP_ATA_PASSTHROUGH12 and SCSIOP_ATA_PASSTHROUGH16). + */ +/*@{*/ +#define ATA_PASSTHROUGH_PROTOCOL_HARDWARE_RESET 0x0 +#define ATA_PASSTHROUGH_PROTOCOL_SOFTWARE_RESET 0x1 +#define ATA_PASSTHROUGH_PROTOCOL_NON_DATA 0x3 +#define ATA_PASSTHROUGH_PROTOCOL_PIO_DATA_IN 0x4 +#define ATA_PASSTHROUGH_PROTOCOL_PIO_DATA_OUT 0x5 +#define ATA_PASSTHROUGH_PROTOCOL_DMA 0x6 +#define ATA_PASSTHROUGH_PROTOCOL_DEVICE_DIAG 0x8 +#define ATA_PASSTHROUGH_PROTOCOL_DEVICE_RESET 0x9 +#define ATA_PASSTHROUGH_PROTOCOL_UDMA_DATA_IN 0xA +#define ATA_PASSTHROUGH_PROTOCOL_UDMA_DATA_OUT 0xB +#define ATA_PASSTHROUGH_PROTOCOL_NCQ 0xC +#define ATA_PASSTHROUGH_PROTOCOL_RETURN_RESPONSE 0xF +/*@}*/ + +/** + * Additional sense code for the successfully completed ATA passthrough commands + * with check condition enabled. + */ +#define SCSI_SENSEQ_ATA_PASS_THROUGH_INFORMATION_AVAILABLE 0x1D + +/** + * @brief + * Handler for the IOCTL_ATA_PASS_THROUGH and IOCTL_ATA_PASS_THROUGH_DIRECT requests. + * + * @param[in] DeviceObject + * PDO device object. + * + * @param[in,out] Irp + * Pointer to the IOCTL request. + * + * @param[in] MaximumTransferLength + * Maximum size of data transfer for a device. + * + * @param[in] MaximumPhysicalPages + * Maximum number of physical pages per data transfer for a device. + * + * @return Status. + */ +CODE_SEG("PAGE") +NTSTATUS +SptiHandleAtaPassthru( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp, + _In_ ULONG MaximumTransferLength, + _In_ ULONG MaximumPhysicalPages); + +/** + * @brief + * Handler for the IOCTL_SCSI_PASS_THROUGH and IOCTL_SCSI_PASS_THROUGH_DIRECT requests. + * + * @param[in] DeviceObject + * PDO device object. + * + * @param[in,out] Irp + * Pointer to the IOCTL request. + * + * @param[in] MaximumTransferLength + * Maximum size of data transfer for a device. + * + * @param[in] MaximumPhysicalPages + * Maximum number of physical pages per data transfer for a device. + * + * @return Status. + */ +CODE_SEG("PAGE") +NTSTATUS +SptiHandleScsiPassthru( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp, + _In_ ULONG MaximumTransferLength, + _In_ ULONG MaximumPhysicalPages); diff --git a/sdk/lib/drivers/sptilib/sptilibp.h b/sdk/lib/drivers/sptilib/sptilibp.h new file mode 100644 index 00000000000..7f0f0e4eb27 --- /dev/null +++ b/sdk/lib/drivers/sptilib/sptilibp.h @@ -0,0 +1,35 @@ +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Private header of the ATA and SCSI Pass Through Interface for storage drivers + * COPYRIGHT: Copyright 2025 Dmitry Borisov + */ + +#pragma once + +/** + * SPTI allocation tag. + */ +#define TAG_SPTI 'ITPS' + +/** + * Timeouts to wait for a request to be completed, in seconds. + */ +/*@{*/ +#define PASSTHROUGH_CMD_TIMEOUT_MIN_SEC 1 +#define PASSTHROUGH_CMD_TIMEOUT_MAX_SEC (30 * 60 * 60) // 30 hours +/*@}*/ + +#define GET_IOCTL(IoStack) ((IoStack)->Parameters.DeviceIoControl.IoControlCode) + +typedef union _PASSTHROUGH_DATA +{ + PVOID Buffer; + ULONG_PTR BufferOffset; +} PASSTHROUGH_DATA, *PPASSTHROUGH_DATA; + +typedef struct _PASSTHROUGH_IRP_CONTEXT +{ + SCSI_REQUEST_BLOCK Srb; + PIRP Irp; +} PASSTHROUGH_IRP_CONTEXT, *PPASSTHROUGH_IRP_CONTEXT;