You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1164 lines
31 KiB
1164 lines
31 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1990 - 1999
|
|
|
|
Module Name:
|
|
|
|
passthru.c
|
|
|
|
Abstract:
|
|
|
|
This is the storage port driver library. This file contains code that
|
|
implements SCSI passthrough.
|
|
|
|
Authors:
|
|
|
|
John Strange (JohnStra)
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module implements SCSI passthru for storage port drivers.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
//
|
|
// Constants and macros to enforce good use of Ex[Allocate|Free]PoolWithTag.
|
|
// Remeber that all pool tags will display in the debugger in reverse order
|
|
//
|
|
|
|
#if USE_EXFREEPOOLWITHTAG_ONLY
|
|
#define TAG(x) (x | 0x80000000)
|
|
#else
|
|
#define TAG(x) (x)
|
|
#endif
|
|
|
|
#define PORT_TAG_SENSE_BUFFER TAG('iPlP') // Sense info
|
|
|
|
#define PORT_IS_COPY(Srb) \
|
|
((Srb)->Cdb[0] == SCSIOP_COPY)
|
|
#define PORT_IS_COMPARE(Srb) \
|
|
((Srb)->Cdb[0] == SCSIOP_COMPARE)
|
|
#define PORT_IS_COPY_COMPARE(Srb) \
|
|
((Srb)->Cdb[0] == SCSIOP_COPY_COMPARE)
|
|
|
|
#define PORT_IS_ILLEGAL_PASSTHROUGH_COMMAND(Srb) \
|
|
(PORT_IS_COPY((Srb)) || \
|
|
PORT_IS_COMPARE((Srb)) || \
|
|
PORT_IS_COPY_COMPARE((Srb)))
|
|
|
|
#if defined (_WIN64)
|
|
NTSTATUS
|
|
PortpTranslatePassThrough32To64(
|
|
IN PSCSI_PASS_THROUGH32 SrbControl32,
|
|
IN OUT PSCSI_PASS_THROUGH SrbControl64
|
|
);
|
|
|
|
VOID
|
|
PortpTranslatePassThrough64To32(
|
|
IN PSCSI_PASS_THROUGH SrbControl64,
|
|
IN OUT PSCSI_PASS_THROUGH32 SrbControl32
|
|
);
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PortpSendValidPassThrough(
|
|
IN PPORT_PASSTHROUGH_INFO PassThroughInfo,
|
|
IN PIRP RequestIrp,
|
|
IN ULONG SrbFlags,
|
|
IN BOOLEAN Direct
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PortGetPassThrough)
|
|
#pragma alloc_text(PAGE, PortPassThroughInitialize)
|
|
#pragma alloc_text(PAGE, PortPassThroughInitializeSrb)
|
|
#pragma alloc_text(PAGE, PortSendPassThrough)
|
|
#pragma alloc_text(PAGE, PortpSendValidPassThrough)
|
|
#pragma alloc_text(PAGE, PortPassThroughCleanup)
|
|
#pragma alloc_text(PAGE, PortGetPassThroughAddress)
|
|
#pragma alloc_text(PAGE, PortSetPassThroughAddress)
|
|
#pragma alloc_text(PAGE, PortPassThroughMarshalResults)
|
|
#if defined (_WIN64)
|
|
#pragma alloc_text(PAGE, PortpTranslatePassThrough32To64)
|
|
#pragma alloc_text(PAGE, PortpTranslatePassThrough64To32)
|
|
#endif
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PortGetPassThroughAddress(
|
|
IN PIRP Irp,
|
|
OUT PUCHAR PathId,
|
|
OUT PUCHAR TargetId,
|
|
OUT PUCHAR Lun
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the address of the device to witch the passthrough
|
|
request is to be sent.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies a pointer to the IRP that contains the
|
|
SCSI_PASS_THROUGH structure.
|
|
|
|
PathId - Pointer to the PathId of the addressed device.
|
|
|
|
TargetId - Pointer to the TargetId of the addressed device.
|
|
|
|
Lun - Pointer to the logical unit number of the addressed device.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_PASS_THROUGH srbControl = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG requiredSize;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if defined(_WIN64)
|
|
if (IoIs32bitProcess(Irp)) {
|
|
requiredSize = sizeof(SCSI_PASS_THROUGH32);
|
|
} else {
|
|
requiredSize = sizeof(SCSI_PASS_THROUGH);
|
|
}
|
|
#else
|
|
requiredSize = sizeof(SCSI_PASS_THROUGH);
|
|
#endif
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
requiredSize) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*PathId = srbControl->PathId;
|
|
*TargetId = srbControl->TargetId;
|
|
*Lun = srbControl->Lun;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PortSetPassThroughAddress(
|
|
IN PIRP Irp,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the address of the SCSI_PASS_THROUGH structure
|
|
embedded in the supplied IRP.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies a pointer to the IRP that contains the
|
|
SCSI_PASS_THROUGH structure.
|
|
|
|
PathId - The PathId of the addressed device.
|
|
|
|
TargetId - The TargetId of the addressed device.
|
|
|
|
Lun - The logical unit number of the addressed device.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_PASS_THROUGH srbControl = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG requiredSize;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if defined(_WIN64)
|
|
if (IoIs32bitProcess(Irp)) {
|
|
requiredSize = sizeof(SCSI_PASS_THROUGH32);
|
|
} else {
|
|
requiredSize = sizeof(SCSI_PASS_THROUGH);
|
|
}
|
|
#else
|
|
requiredSize = sizeof(SCSI_PASS_THROUGH);
|
|
#endif
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
requiredSize) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
srbControl->PathId = PathId;
|
|
srbControl->TargetId = TargetId;
|
|
srbControl->Lun = Lun;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PortSendPassThrough (
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP RequestIrp,
|
|
IN BOOLEAN Direct,
|
|
IN ULONG SrbFlags,
|
|
IN PIO_SCSI_CAPABILITIES Capabilities
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sends a user specified SCSI CDB to the device identified in
|
|
the supplied SCSI_PASS_THROUGH structure. It creates an srb which is
|
|
processed normally by the port driver. This call is synchornous.
|
|
|
|
Arguments:
|
|
|
|
Pdo - Supplies a pointer to the PDO to which the passthrough
|
|
command will be dispatched.
|
|
|
|
RequestIrp - Supplies a pointer to the IRP that made the original
|
|
request.
|
|
|
|
Direct - Boolean indicating whether this is a direct passthrough.
|
|
|
|
SrbFlags - The flags to copy into the SRB used for this command.
|
|
|
|
Capabilities - Supplies a pointer to the IO_SCSI_CAPABILITIES structure
|
|
describing the storage adapter.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PORT_PASSTHROUGH_INFO passThroughInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory(&passThroughInfo, sizeof(PORT_PASSTHROUGH_INFO));
|
|
|
|
//
|
|
// Try to init a pointer to the passthrough structure in the IRP.
|
|
//
|
|
|
|
status = PortGetPassThrough(
|
|
&passThroughInfo,
|
|
RequestIrp,
|
|
Direct
|
|
);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Perform parameter checking and to setup the PORT_PASSTHROUGH_INFO
|
|
// structure.
|
|
//
|
|
|
|
status = PortPassThroughInitialize(
|
|
&passThroughInfo,
|
|
RequestIrp,
|
|
Capabilities,
|
|
Pdo,
|
|
Direct
|
|
);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Call helper routine to finish processing the passthrough request.
|
|
//
|
|
|
|
status = PortpSendValidPassThrough(
|
|
&passThroughInfo,
|
|
RequestIrp,
|
|
SrbFlags,
|
|
Direct
|
|
);
|
|
}
|
|
|
|
PortPassThroughCleanup(&passThroughInfo);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PortGetPassThrough(
|
|
IN OUT PPORT_PASSTHROUGH_INFO PassThroughInfo,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN Direct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a pointer to the user supplied SCSI_PASS_THROUGH
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure.
|
|
|
|
Irp - Supplies a pointer to the IRP.
|
|
|
|
Direct - Supplies a boolean that indicates whether this is a
|
|
SCSI_PASS_THROUGH_DIRECT request.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
ULONG inputLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a pointer to the pass through structure.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PassThroughInfo->SrbControl = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// BUGBUG: Why do we need to save this pointer to the beginning of
|
|
// the SCSI_PASS_THROUGH structure.
|
|
//
|
|
|
|
PassThroughInfo->SrbBuffer = (PVOID) PassThroughInfo->SrbControl;
|
|
|
|
//
|
|
// Initialize a stack variable to hold the size of the input buffer.
|
|
//
|
|
|
|
inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
//
|
|
// For WIN64, a passthrough request from a 32-bit application requires
|
|
// us to perform a translation on the supplied SCSI_PASS_THROUGH structure.
|
|
// This is required because the layout of the 32-bit structure does not
|
|
// match that of a 64-bit structure. In this case, we translate the
|
|
// supplied 32-bit structure into a stack-allocated 64-bit structure which
|
|
// will be used to process the pass through request.
|
|
//
|
|
|
|
#if defined (_WIN64)
|
|
if (IoIs32bitProcess(Irp)) {
|
|
|
|
if (inputLength < sizeof(SCSI_PASS_THROUGH32)){
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PassThroughInfo->SrbControl32 =
|
|
(PSCSI_PASS_THROUGH32)(Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
if (Direct == FALSE) {
|
|
if (PassThroughInfo->SrbControl32->Length !=
|
|
sizeof(SCSI_PASS_THROUGH32)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
} else {
|
|
if (PassThroughInfo->SrbControl32->Length !=
|
|
sizeof(SCSI_PASS_THROUGH_DIRECT32)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
}
|
|
|
|
status = PortpTranslatePassThrough32To64(
|
|
PassThroughInfo->SrbControl32,
|
|
&PassThroughInfo->SrbControl64);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
PassThroughInfo->SrbControl = &PassThroughInfo->SrbControl64;
|
|
|
|
} else {
|
|
#endif
|
|
if (inputLength < sizeof(SCSI_PASS_THROUGH)){
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (Direct == FALSE) {
|
|
if (PassThroughInfo->SrbControl->Length !=
|
|
sizeof(SCSI_PASS_THROUGH)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
} else {
|
|
if (PassThroughInfo->SrbControl->Length !=
|
|
sizeof(SCSI_PASS_THROUGH_DIRECT)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
}
|
|
#if defined (_WIN64)
|
|
}
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PortPassThroughInitialize(
|
|
IN OUT PPORT_PASSTHROUGH_INFO PassThroughInfo,
|
|
IN PIRP Irp,
|
|
IN PIO_SCSI_CAPABILITIES Capabilities,
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN BOOLEAN Direct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates the caller-supplied data and initializes the
|
|
PORT_PASSTHROUGH_INFO structure.
|
|
|
|
Arguments:
|
|
|
|
PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure.
|
|
|
|
Irp - Supplies a pointer to the IRP.
|
|
|
|
Direct - Supplies a boolean that indicates whether this is a
|
|
SCSI_PASS_THROUGH_DIRECT request.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG outputLength;
|
|
ULONG inputLength;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_PASS_THROUGH srbControl;
|
|
ULONG dataTransferLength;
|
|
ULONG pages;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
srbControl = PassThroughInfo->SrbControl;
|
|
outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
PassThroughInfo->Pdo = Pdo;
|
|
|
|
//
|
|
// Verify that the CDB is no greater than 16 bytes.
|
|
//
|
|
|
|
if (srbControl->CdbLength > 16) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// If there's a sense buffer then its offset cannot be shorter than the
|
|
// length of the srbControl block, nor can it be located after the data
|
|
// buffer (if any).
|
|
//
|
|
|
|
if (srbControl->SenseInfoLength != 0) {
|
|
|
|
//
|
|
// Sense info offset should not be smaller than the size of the
|
|
// pass through structure.
|
|
//
|
|
|
|
if (srbControl->Length > srbControl->SenseInfoOffset) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!Direct) {
|
|
|
|
//
|
|
// Sense info buffer should precede the data buffer offset.
|
|
//
|
|
|
|
if ((srbControl->SenseInfoOffset >= srbControl->DataBufferOffset) ||
|
|
((srbControl->SenseInfoOffset + srbControl->SenseInfoLength) >
|
|
srbControl->DataBufferOffset)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Sense info buffer should be within the output buffer.
|
|
//
|
|
|
|
if ((srbControl->SenseInfoOffset > outputLength) ||
|
|
(srbControl->SenseInfoOffset + srbControl->SenseInfoLength >
|
|
outputLength)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Sense info buffer should be within the output buffer.
|
|
//
|
|
|
|
if ((srbControl->SenseInfoOffset > outputLength) ||
|
|
(srbControl->SenseInfoOffset + srbControl->SenseInfoLength >
|
|
outputLength)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Direct) {
|
|
|
|
//
|
|
// Data buffer offset should be greater than the size of the pass
|
|
// through structure.
|
|
//
|
|
|
|
if (srbControl->Length > srbControl->DataBufferOffset &&
|
|
srbControl->DataTransferLength != 0) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// If this command is sending data to the device. Make sure the data
|
|
// buffer lies entirely within the supplied input buffer.
|
|
//
|
|
|
|
if (srbControl->DataIn != SCSI_IOCTL_DATA_IN) {
|
|
|
|
if ((srbControl->DataBufferOffset > inputLength) ||
|
|
((srbControl->DataBufferOffset +
|
|
srbControl->DataTransferLength) >
|
|
inputLength)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this command is retrieving data from the device, make sure the
|
|
// data buffer lies entirely within the supplied output buffer.
|
|
//
|
|
|
|
if (srbControl->DataIn) {
|
|
|
|
if ((srbControl->DataBufferOffset > outputLength) ||
|
|
((srbControl->DataBufferOffset +
|
|
srbControl->DataTransferLength) >
|
|
outputLength)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate the specified timeout value.
|
|
//
|
|
|
|
if (srbControl->TimeOutValue == 0 ||
|
|
srbControl->TimeOutValue > 30 * 60 * 60) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check for illegal command codes.
|
|
//
|
|
|
|
if (PORT_IS_ILLEGAL_PASSTHROUGH_COMMAND(srbControl)) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if (srbControl->DataTransferLength == 0) {
|
|
|
|
PassThroughInfo->Length = 0;
|
|
PassThroughInfo->Buffer = NULL;
|
|
PassThroughInfo->BufferOffset = 0;
|
|
PassThroughInfo->MajorCode = IRP_MJ_FLUSH_BUFFERS;
|
|
|
|
} else if (Direct == TRUE) {
|
|
|
|
PassThroughInfo->Length = (ULONG) srbControl->DataTransferLength;
|
|
PassThroughInfo->Buffer = (PUCHAR) srbControl->DataBufferOffset;
|
|
PassThroughInfo->BufferOffset = 0;
|
|
PassThroughInfo->MajorCode = !srbControl->DataIn ? IRP_MJ_WRITE :
|
|
IRP_MJ_READ;
|
|
|
|
} else {
|
|
|
|
PassThroughInfo->Length = (ULONG) srbControl->DataBufferOffset +
|
|
srbControl->DataTransferLength;
|
|
PassThroughInfo->Buffer = (PUCHAR) PassThroughInfo->SrbBuffer;
|
|
PassThroughInfo->BufferOffset = (ULONG)srbControl->DataBufferOffset;
|
|
PassThroughInfo->MajorCode = !srbControl->DataIn ? IRP_MJ_WRITE :
|
|
IRP_MJ_READ;
|
|
}
|
|
|
|
//
|
|
// Make sure the buffer is properly aligned.
|
|
//
|
|
|
|
if (Direct == TRUE) {
|
|
|
|
//
|
|
// Make sure the user buffer is valid. IoBuildSynchronouseFsdRequest
|
|
// calls MmProbeAndLock with AccessMode == KernelMode. This check is
|
|
// the extra stuff that MM would do if it were called with
|
|
// AccessMode == UserMode.
|
|
//
|
|
// ISSUE: We should probably do an MmProbeAndLock here.
|
|
//
|
|
|
|
if (Irp->RequestorMode != KernelMode) {
|
|
PVOID endByte;
|
|
if (PassThroughInfo->Length) {
|
|
endByte = (PVOID)((PCHAR)PassThroughInfo->Buffer +
|
|
PassThroughInfo->Length - 1);
|
|
if ((endByte > (PVOID)MM_HIGHEST_USER_ADDRESS) ||
|
|
(PassThroughInfo->Buffer >= endByte)) {
|
|
return STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (srbControl->DataBufferOffset &
|
|
PassThroughInfo->Pdo->AlignmentRequirement) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the request is too big for the adapter.
|
|
//
|
|
|
|
dataTransferLength = PassThroughInfo->SrbControl->DataTransferLength;
|
|
|
|
pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
(PUCHAR)PassThroughInfo->Buffer + PassThroughInfo->BufferOffset,
|
|
dataTransferLength);
|
|
|
|
if ((dataTransferLength != 0) &&
|
|
(pages > Capabilities->MaximumPhysicalPages ||
|
|
dataTransferLength > Capabilities->MaximumTransferLength)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PortPassThroughCleanup(
|
|
IN PPORT_PASSTHROUGH_INFO PassThroughInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs any cleanup required after processing a SCSI
|
|
passthrough request.
|
|
|
|
Arguments:
|
|
|
|
PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
#if defined (_WIN64)
|
|
if (PassThroughInfo->SrbControl32 != NULL) {
|
|
PortpTranslatePassThrough64To32(
|
|
PassThroughInfo->SrbControl,
|
|
PassThroughInfo->SrbControl32);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
NTSTATUS
|
|
PortPassThroughInitializeSrb(
|
|
IN PPORT_PASSTHROUGH_INFO PassThroughInfo,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PIRP Irp,
|
|
IN ULONG SrbFlags,
|
|
IN PVOID SenseBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the supplied SRB to send a passthrough request.
|
|
|
|
Arguments:
|
|
|
|
PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure.
|
|
|
|
Srb - Supplies a pointer to the SRB to initialize.
|
|
|
|
Irp - Supplies a pointer to the IRP.
|
|
|
|
SrbFlags - Supplies the appropriate SRB flags for the request.
|
|
|
|
SenseBuffer - Supplies a pointer to request sense buffer to put in
|
|
the SRB.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
PSCSI_PASS_THROUGH srbControl;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Zero out the srb.
|
|
//
|
|
|
|
RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
//
|
|
// Fill in the srb.
|
|
//
|
|
|
|
srbControl = PassThroughInfo->SrbControl;
|
|
Srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
Srb->SrbStatus = SRB_STATUS_PENDING;
|
|
Srb->PathId = srbControl->PathId;
|
|
Srb->TargetId = srbControl->TargetId;
|
|
Srb->Lun = srbControl->Lun;
|
|
Srb->CdbLength = srbControl->CdbLength;
|
|
Srb->SenseInfoBufferLength = srbControl->SenseInfoLength;
|
|
|
|
switch (srbControl->DataIn) {
|
|
case SCSI_IOCTL_DATA_OUT:
|
|
if (srbControl->DataTransferLength) {
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
|
|
}
|
|
break;
|
|
|
|
case SCSI_IOCTL_DATA_IN:
|
|
if (srbControl->DataTransferLength) {
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_IN;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT;
|
|
break;
|
|
}
|
|
|
|
if (srbControl->DataTransferLength == 0) {
|
|
|
|
Srb->SrbFlags = 0;
|
|
} else {
|
|
|
|
//
|
|
// Flush the data buffer for output. This will insure that the data is
|
|
// written back to memory.
|
|
//
|
|
|
|
if (Irp != NULL) {
|
|
KeFlushIoBuffers(Irp->MdlAddress, FALSE, TRUE);
|
|
}
|
|
}
|
|
|
|
Srb->SrbFlags |= (SrbFlags | SRB_FLAGS_NO_QUEUE_FREEZE);
|
|
Srb->DataTransferLength = srbControl->DataTransferLength;
|
|
Srb->TimeOutValue = srbControl->TimeOutValue;
|
|
Srb->DataBuffer = (PCHAR) PassThroughInfo->Buffer +
|
|
PassThroughInfo->BufferOffset;
|
|
Srb->SenseInfoBuffer = SenseBuffer;
|
|
Srb->OriginalRequest = Irp;
|
|
|
|
RtlCopyMemory(Srb->Cdb, srbControl->Cdb, srbControl->CdbLength);
|
|
|
|
//
|
|
// Disable autosense if there's no sense buffer to put the data in.
|
|
//
|
|
|
|
if (SenseBuffer == NULL) {
|
|
Srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#if defined (_WIN64)
|
|
NTSTATUS
|
|
PortpTranslatePassThrough32To64(
|
|
IN PSCSI_PASS_THROUGH32 SrbControl32,
|
|
IN OUT PSCSI_PASS_THROUGH SrbControl64
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
On WIN64, a SCSI_PASS_THROUGH structure sent down by a 32-bit application
|
|
must be marshaled into a 64-bit version of the structure. This function
|
|
performs that marshaling.
|
|
|
|
Arguments:
|
|
|
|
SrbControl32 - Supplies a pointer to a 32-bit SCSI_PASS_THROUGH struct.
|
|
|
|
SrbControl64 - Supplies the address of a pointer to a 64-bit
|
|
SCSI_PASS_THROUGH structure, into which we'll copy the
|
|
marshaled 32-bit data.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy the first set of fields out of the 32-bit structure. These
|
|
// fields all line up between the 32 & 64 bit versions.
|
|
//
|
|
// Note that we do NOT adjust the length in the srbControl. This is to
|
|
// allow the calling routine to compare the length of the actual
|
|
// control area against the offsets embedded within. If we adjusted the
|
|
// length then requests with the sense area backed against the control
|
|
// area would be rejected because the 64-bit control area is 4 bytes
|
|
// longer.
|
|
//
|
|
|
|
RtlCopyMemory(SrbControl64,
|
|
SrbControl32,
|
|
FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)
|
|
);
|
|
|
|
//
|
|
// Copy over the CDB.
|
|
//
|
|
|
|
RtlCopyMemory(SrbControl64->Cdb,
|
|
SrbControl32->Cdb,
|
|
16 * sizeof(UCHAR)
|
|
);
|
|
|
|
//
|
|
// Copy the fields that follow the ULONG_PTR.
|
|
//
|
|
|
|
SrbControl64->DataBufferOffset = (ULONG_PTR)SrbControl32->DataBufferOffset;
|
|
SrbControl64->SenseInfoOffset = SrbControl32->SenseInfoOffset;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
PortpTranslatePassThrough64To32(
|
|
IN PSCSI_PASS_THROUGH SrbControl64,
|
|
IN OUT PSCSI_PASS_THROUGH32 SrbControl32
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
On WIN64, a SCSI_PASS_THROUGH structure sent down by a 32-bit application
|
|
must be marshaled into a 64-bit version of the structure. This function
|
|
marshals a 64-bit version of the structure back into a 32-bit version.
|
|
|
|
Arguments:
|
|
|
|
SrbControl64 - Supplies a pointer to a 64-bit SCSI_PASS_THROUGH struct.
|
|
|
|
SrbControl32 - Supplies the address of a pointer to a 32-bit
|
|
SCSI_PASS_THROUGH structure, into which we'll copy the
|
|
marshaled 64-bit data.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy back the fields through the data offsets.
|
|
//
|
|
|
|
RtlCopyMemory(SrbControl32,
|
|
SrbControl64,
|
|
FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PortpSendValidPassThrough(
|
|
IN PPORT_PASSTHROUGH_INFO PassThroughInfo,
|
|
IN PIRP RequestIrp,
|
|
IN ULONG SrbFlags,
|
|
IN BOOLEAN Direct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the SCSI passthrough request described by the supplied
|
|
PORT_PASSTHROUGH_INFO.
|
|
|
|
Arguments:
|
|
|
|
PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure.
|
|
RequestIrp - Supplies a pointer to the IRP.
|
|
SrbFlags - Supplies the appropriate SRB flags for the request.
|
|
Direct - Indicates whether this is a SCSO_PASS_THROUGH_DIRECT.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status;
|
|
PIRP irp;
|
|
PVOID senseBuffer;
|
|
KEVENT event;
|
|
LARGE_INTEGER startingOffset;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate an aligned sense buffer if one is required.
|
|
//
|
|
|
|
if (PassThroughInfo->SrbControl->SenseInfoLength != 0) {
|
|
|
|
senseBuffer = ExAllocatePoolWithTag(
|
|
NonPagedPoolCacheAligned,
|
|
PassThroughInfo->SrbControl->SenseInfoLength,
|
|
PORT_TAG_SENSE_BUFFER
|
|
);
|
|
|
|
if (senseBuffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
} else {
|
|
|
|
senseBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Must be at PASSIVE_LEVEL to use synchronous FSD.
|
|
//
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
//
|
|
// Initialize the notification event.
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
// Note we do this synchronously for two reasons. If it was done
|
|
// asynchonously then the completion code would have to make a special
|
|
// check to deallocate the buffer. Second if a completion routine were
|
|
// used then an addation stack locate would be needed.
|
|
//
|
|
|
|
startingOffset.QuadPart = (LONGLONG) 1;
|
|
|
|
irp = IoBuildSynchronousFsdRequest(
|
|
PassThroughInfo->MajorCode,
|
|
PassThroughInfo->Pdo,
|
|
PassThroughInfo->Buffer,
|
|
PassThroughInfo->Length,
|
|
&startingOffset,
|
|
&event,
|
|
&ioStatusBlock);
|
|
|
|
if (irp == NULL) {
|
|
|
|
if (senseBuffer != NULL) {
|
|
ExFreePool(senseBuffer);
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Set major code.
|
|
//
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
irpStack->MinorFunction = 1;
|
|
|
|
//
|
|
// Initialize the IRP stack location to point to the SRB.
|
|
//
|
|
|
|
irpStack->Parameters.Others.Argument1 = &srb;
|
|
|
|
//
|
|
// Have the port library initialize the SRB for us.
|
|
//
|
|
|
|
status = PortPassThroughInitializeSrb(
|
|
PassThroughInfo,
|
|
&srb,
|
|
irp,
|
|
SrbFlags,
|
|
senseBuffer);
|
|
|
|
//
|
|
// Call port driver to handle this request and wait for the request to
|
|
// complete.
|
|
//
|
|
|
|
status = IoCallDriver(PassThroughInfo->Pdo, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
} else {
|
|
|
|
ioStatusBlock.Status = status;
|
|
}
|
|
|
|
PortPassThroughMarshalResults(PassThroughInfo,
|
|
&srb,
|
|
RequestIrp,
|
|
&ioStatusBlock,
|
|
Direct);
|
|
|
|
//
|
|
// Free the sense buffer.
|
|
//
|
|
|
|
if (senseBuffer != NULL) {
|
|
ExFreePool(senseBuffer);
|
|
}
|
|
|
|
return ioStatusBlock.Status;
|
|
}
|
|
|
|
VOID
|
|
PortPassThroughMarshalResults(
|
|
IN PPORT_PASSTHROUGH_INFO PassThroughInfo,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PIRP RequestIrp,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN BOOLEAN Direct
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy the returned values from the srb to the control structure.
|
|
//
|
|
|
|
PassThroughInfo->SrbControl->ScsiStatus = Srb->ScsiStatus;
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
|
|
|
//
|
|
// Set the status to success so that the data is returned.
|
|
//
|
|
|
|
IoStatusBlock->Status = STATUS_SUCCESS;
|
|
PassThroughInfo->SrbControl->SenseInfoLength =
|
|
Srb->SenseInfoBufferLength;
|
|
|
|
//
|
|
// Copy the sense data to the system buffer.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
(PUCHAR)PassThroughInfo->SrbBuffer +
|
|
PassThroughInfo->SrbControl->SenseInfoOffset,
|
|
Srb->SenseInfoBuffer,
|
|
Srb->SenseInfoBufferLength);
|
|
|
|
} else {
|
|
PassThroughInfo->SrbControl->SenseInfoLength = 0;
|
|
}
|
|
|
|
//
|
|
// If the srb status is buffer underrun then set the status to success.
|
|
// This insures that the data will be returned to the caller.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
IoStatusBlock->Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set the information length
|
|
//
|
|
|
|
PassThroughInfo->SrbControl->DataTransferLength = Srb->DataTransferLength;
|
|
|
|
if (Direct == TRUE) {
|
|
|
|
RequestIrp->IoStatus.Information =
|
|
PassThroughInfo->SrbControl->SenseInfoOffset +
|
|
PassThroughInfo->SrbControl->SenseInfoLength;
|
|
} else {
|
|
|
|
if (!PassThroughInfo->SrbControl->DataIn ||
|
|
PassThroughInfo->BufferOffset == 0) {
|
|
|
|
RequestIrp->IoStatus.Information =
|
|
PassThroughInfo->SrbControl->SenseInfoOffset +
|
|
PassThroughInfo->SrbControl->SenseInfoLength;
|
|
|
|
} else {
|
|
|
|
RequestIrp->IoStatus.Information =
|
|
PassThroughInfo->SrbControl->DataBufferOffset +
|
|
PassThroughInfo->SrbControl->DataTransferLength;
|
|
}
|
|
}
|
|
|
|
ASSERT((Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) == 0);
|
|
|
|
RequestIrp->IoStatus.Status = IoStatusBlock->Status;
|
|
|
|
return;
|
|
}
|