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.
1274 lines
30 KiB
1274 lines
30 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 2002
|
|
|
|
Module Name:
|
|
|
|
passthru.c
|
|
|
|
Abstract:
|
|
|
|
This file contains routines to handle IOCTL_ATA_PASS_THROUGH
|
|
|
|
Authors:
|
|
|
|
Krishnan Varadarajan (krishvar)
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module implements ATA passthru.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ideport.h"
|
|
|
|
#define DataIn(ataPassThrough) \
|
|
(ataPassThrough->AtaFlags & ATA_FLAGS_DATA_IN)
|
|
|
|
#define DataOut(ataPassThrough) \
|
|
(ataPassThrough->AtaFlags & ATA_FLAGS_DATA_OUT)
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughValidateInput (
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN Direct
|
|
);
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughSyncCompletion (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
IdeAtaPassThroughMarshalResults(
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PATA_PASS_THROUGH_EX AtaPassThrough,
|
|
IN BOOLEAN Direct,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
#if defined (_WIN64)
|
|
VOID
|
|
IdeTranslateAtaPassThrough32To64(
|
|
IN PATA_PASS_THROUGH_EX32 AtaPassThrough32,
|
|
IN OUT PATA_PASS_THROUGH_EX AtaPassThrough64
|
|
);
|
|
|
|
VOID
|
|
IdeTranslateAtaPassThrough64To32(
|
|
IN PATA_PASS_THROUGH_EX AtaPassThrough64,
|
|
IN OUT PATA_PASS_THROUGH_EX32 AtaPassThrough32
|
|
);
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IdeHandleAtaPassThroughIoctl)
|
|
#pragma alloc_text(PAGE, IdeAtaPassThroughSetPortAddress)
|
|
#pragma alloc_text(PAGE, IdeAtaPassThroughGetPortAddress)
|
|
#pragma alloc_text(PAGE, IdeAtaPassThroughValidateInput)
|
|
#pragma alloc_text(PAGE, IdeAtaPassThroughSendSynchronous)
|
|
#pragma alloc_text(PAGE, IdeAtaPassThroughMarshalResults)
|
|
#if defined (_WIN64)
|
|
#pragma alloc_text(PAGE, IdeTranslateAtaPassThrough32To64)
|
|
#pragma alloc_text(PAGE, IdeTranslateAtaPassThrough64To32)
|
|
#endif
|
|
#endif
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughSetPortAddress (
|
|
PIRP Irp,
|
|
UCHAR PathId,
|
|
UCHAR TargetId,
|
|
UCHAR Lun
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the address fields in the ataPassThrough structure embedded in
|
|
the irp.
|
|
|
|
Arguments:
|
|
|
|
Irp : The ata passthrough irp
|
|
PathId : PathId of the pdo.
|
|
TargetId : Pdo's targetId.
|
|
Lun : Lun represented by the pdo.
|
|
|
|
Return Valud:
|
|
|
|
STATUS_SUCCESS if the operation succeeded.
|
|
STATUS_INVALID_PARAMETER otherwise.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
ULONG requiredSize;
|
|
ULONG inputLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
requiredSize = sizeof(ATA_PASS_THROUGH_EX);
|
|
|
|
#if defined (_WIN64)
|
|
if (IoIs32bitProcess(Irp)) {
|
|
requiredSize = sizeof(ATA_PASS_THROUGH_EX32);
|
|
}
|
|
#endif
|
|
|
|
if (inputLength < requiredSize) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PATA_PASS_THROUGH_EX ataPassThrough;
|
|
|
|
ataPassThrough = Irp->AssociatedIrp.SystemBuffer;
|
|
ataPassThrough->PathId = PathId;
|
|
ataPassThrough->TargetId = TargetId;
|
|
ataPassThrough->Lun = Lun;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughGetAddress(
|
|
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);
|
|
NTSTATUS status;
|
|
ULONG requiredSize;
|
|
ULONG inputLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
requiredSize = sizeof(ATA_PASS_THROUGH_EX);
|
|
|
|
#if defined (_WIN64)
|
|
if (IoIs32bitProcess(Irp)) {
|
|
requiredSize = sizeof(ATA_PASS_THROUGH_EX32);
|
|
}
|
|
#endif
|
|
|
|
if (inputLength < requiredSize) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PATA_PASS_THROUGH_EX ataPassThrough;
|
|
|
|
ataPassThrough = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
*PathId = ataPassThrough->PathId;
|
|
*TargetId = ataPassThrough->TargetId;
|
|
*Lun = ataPassThrough->Lun;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeHandleAtaPassThroughIoctl (
|
|
PFDO_EXTENSION FdoExtension,
|
|
PIRP RequestIrp,
|
|
BOOLEAN Direct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles IOCTL_ATA_PASS_THROUGH and its DIRECT version.
|
|
|
|
Arguments:
|
|
|
|
FdoExtension :
|
|
|
|
RequestIrp : The pass through ioctl request
|
|
|
|
Direct : Indicates whether it is direct or not.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN dataIn;
|
|
NTSTATUS status;
|
|
PATA_PASS_THROUGH_EX ataPassThrough;
|
|
PUCHAR passThroughBuffer;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PPDO_EXTENSION pdoExtension;
|
|
UCHAR pathId, targetId, lun;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PUCHAR buffer;
|
|
ULONG bufferOffset;
|
|
ULONG length;
|
|
ULONG pages;
|
|
PIRP irp;
|
|
|
|
#if defined (_WIN64)
|
|
ATA_PASS_THROUGH_EX ataPassThrough64;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
irp = NULL;
|
|
srb = NULL;
|
|
pdoExtension = NULL;
|
|
|
|
//
|
|
// get the device address
|
|
//
|
|
status = IdeAtaPassThroughGetAddress (RequestIrp,
|
|
&pathId,
|
|
&targetId,
|
|
&lun
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// Get a reference to the pdo
|
|
//
|
|
pdoExtension = RefLogicalUnitExtensionWithTag(
|
|
FdoExtension,
|
|
pathId,
|
|
targetId,
|
|
lun,
|
|
FALSE,
|
|
RequestIrp
|
|
);
|
|
|
|
if (pdoExtension == NULL) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// validate the system buffer in the request irp. This comes
|
|
// from user mode. So every parameter needs to be validated.
|
|
//
|
|
status = IdeAtaPassThroughValidateInput (pdoExtension->DeviceObject,
|
|
RequestIrp,
|
|
Direct
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// the system buffer has been validated. Get a pointer to it
|
|
//
|
|
ataPassThrough = RequestIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// we need to keep the pointer to the system buffer since
|
|
// ataPassThrough could be modified to point to a structure
|
|
// allocated on the stack (in WIN64 case)
|
|
//
|
|
passThroughBuffer = (PUCHAR) ataPassThrough;
|
|
|
|
//
|
|
// If the irp is from a 32-bit app running on a 64 bit system
|
|
// then we need to take into account the size difference in the
|
|
// structure. Create a new 64 bit structure and copy over the
|
|
// fields.
|
|
//
|
|
|
|
#if defined (_WIN64)
|
|
if (IoIs32bitProcess(RequestIrp)) {
|
|
|
|
PATA_PASS_THROUGH_EX32 ataPassThrough32;
|
|
|
|
ataPassThrough32 = RequestIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
IdeTranslateAtaPassThrough32To64(
|
|
ataPassThrough32,
|
|
&ataPassThrough64
|
|
);
|
|
|
|
ataPassThrough = &ataPassThrough64;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// determine the transfer length and the data buffer
|
|
//
|
|
if (ataPassThrough->DataTransferLength == 0) {
|
|
|
|
length = 0;
|
|
buffer = NULL;
|
|
bufferOffset = 0;
|
|
|
|
} else if (Direct == TRUE) {
|
|
|
|
length = (ULONG) ataPassThrough->DataTransferLength;
|
|
buffer = (PUCHAR) ataPassThrough->DataBufferOffset;
|
|
bufferOffset = 0;
|
|
|
|
} else {
|
|
|
|
length = (ULONG) ataPassThrough->DataBufferOffset +
|
|
ataPassThrough->DataTransferLength;
|
|
buffer = (PUCHAR) passThroughBuffer;
|
|
bufferOffset = (ULONG)ataPassThrough->DataBufferOffset;
|
|
}
|
|
|
|
//
|
|
// Check if the request is too big for the adapter.
|
|
//
|
|
pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
buffer + bufferOffset,
|
|
ataPassThrough->DataTransferLength);
|
|
|
|
if ((ataPassThrough->DataTransferLength != 0) &&
|
|
((pages > FdoExtension->Capabilities.MaximumPhysicalPages) ||
|
|
(ataPassThrough->DataTransferLength >
|
|
FdoExtension->Capabilities.MaximumTransferLength))) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// setup the irp for ata pass through. The ioctl is method_buffered,
|
|
// but the data buffer could be a user mode one if it is the direct ioctl.
|
|
// determine the access mode accordingly
|
|
//
|
|
irp = IdeAtaPassThroughSetupIrp( pdoExtension->DeviceObject,
|
|
buffer,
|
|
length,
|
|
(Direct ? UserMode : KernelMode),
|
|
DataIn(ataPassThrough) ? TRUE : FALSE
|
|
);
|
|
|
|
if (irp == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// setup the srb. Use the right data offset for the databuffer.
|
|
// Note that the mdl is for the whole buffer (including the header)
|
|
//
|
|
srb = IdeAtaPassThroughSetupSrb (pdoExtension,
|
|
(buffer+bufferOffset),
|
|
ataPassThrough->DataTransferLength,
|
|
ataPassThrough->TimeOutValue,
|
|
ataPassThrough->AtaFlags,
|
|
ataPassThrough->CurrentTaskFile,
|
|
ataPassThrough->PreviousTaskFile
|
|
);
|
|
|
|
if (srb == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// initialize irpstack
|
|
//
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
srb->OriginalRequest = irp;
|
|
|
|
//
|
|
// send it to our pdo synchronously
|
|
//
|
|
status = IdeAtaPassThroughSendSynchronous (pdoExtension->DeviceObject, irp);
|
|
|
|
//
|
|
// set the status
|
|
//
|
|
RequestIrp->IoStatus.Status = status;
|
|
|
|
//
|
|
// marshal the results
|
|
//
|
|
IdeAtaPassThroughMarshalResults (srb,
|
|
ataPassThrough,
|
|
Direct,
|
|
&(RequestIrp->IoStatus)
|
|
);
|
|
|
|
//
|
|
// copy back the results to the original 32 bit
|
|
// structure if necessary
|
|
//
|
|
#if defined (_WIN64)
|
|
if (IoIs32bitProcess(RequestIrp)) {
|
|
|
|
PATA_PASS_THROUGH_EX32 ataPassThrough32;
|
|
|
|
ataPassThrough32 = RequestIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
IdeTranslateAtaPassThrough64To32 (
|
|
ataPassThrough,
|
|
ataPassThrough32
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// return the status of the operation
|
|
//
|
|
status = RequestIrp->IoStatus.Status;
|
|
|
|
GetOut:
|
|
|
|
if (irp) {
|
|
|
|
IdeAtaPassThroughFreeIrp(irp);
|
|
irp = NULL;
|
|
}
|
|
|
|
if (srb) {
|
|
|
|
IdeAtaPassThroughFreeSrb(srb);
|
|
srb = NULL;
|
|
}
|
|
|
|
if (pdoExtension) {
|
|
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
FdoExtension,
|
|
pdoExtension,
|
|
RequestIrp
|
|
);
|
|
pdoExtension = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughValidateInput (
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp,
|
|
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;
|
|
PATA_PASS_THROUGH_EX ataPassThroughEx;
|
|
|
|
#if defined (_WIN64)
|
|
ATA_PASS_THROUGH_EX ataPassThrough64;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
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)) {
|
|
|
|
PATA_PASS_THROUGH_EX32 ataPassThrough32;
|
|
|
|
//
|
|
// the struct should atleast be as big as ATA_PASS_THROUGH_EX32
|
|
//
|
|
if (inputLength < sizeof(ATA_PASS_THROUGH_EX32)){
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ataPassThrough32 = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// The length field should match the size
|
|
// of the structure
|
|
//
|
|
if (Direct == FALSE) {
|
|
|
|
if (ataPassThrough32->Length !=
|
|
sizeof(ATA_PASS_THROUGH_EX32)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ataPassThrough32->Length !=
|
|
sizeof(ATA_PASS_THROUGH_DIRECT32)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
}
|
|
|
|
//
|
|
// translate the structure to the 64 bit version
|
|
//
|
|
IdeTranslateAtaPassThrough32To64(
|
|
ataPassThrough32,
|
|
&ataPassThrough64
|
|
);
|
|
|
|
ataPassThroughEx = &ataPassThrough64;
|
|
|
|
} else {
|
|
#endif
|
|
|
|
//
|
|
// the struct should atleast be as big as ATA_PASS_THROUGH_EX32
|
|
//
|
|
if (inputLength < sizeof(ATA_PASS_THROUGH_EX)){
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
ataPassThroughEx = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// The length field should match the size
|
|
// of the structure
|
|
//
|
|
if (Direct == FALSE) {
|
|
|
|
if (ataPassThroughEx->Length !=
|
|
sizeof(ATA_PASS_THROUGH_EX)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ataPassThroughEx->Length !=
|
|
sizeof(ATA_PASS_THROUGH_DIRECT)) {
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
}
|
|
#if defined (_WIN64)
|
|
}
|
|
#endif
|
|
|
|
if (!Direct) {
|
|
|
|
//
|
|
// Data buffer offset should be greater than the size of the pass
|
|
// through structure.
|
|
//
|
|
|
|
if (ataPassThroughEx->Length > ataPassThroughEx->DataBufferOffset &&
|
|
ataPassThroughEx->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 (DataOut(ataPassThroughEx)) {
|
|
|
|
if ((ataPassThroughEx->DataBufferOffset > inputLength) ||
|
|
((ataPassThroughEx->DataBufferOffset +
|
|
ataPassThroughEx->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 (DataIn(ataPassThroughEx)) {
|
|
|
|
if ((ataPassThroughEx->DataBufferOffset > outputLength) ||
|
|
((ataPassThroughEx->DataBufferOffset +
|
|
ataPassThroughEx->DataTransferLength) >
|
|
outputLength)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make sure the databuffer is properly aligned.
|
|
//
|
|
if (ataPassThroughEx->DataBufferOffset &
|
|
Pdo->AlignmentRequirement) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Validate the specified timeout value.
|
|
//
|
|
|
|
if (ataPassThroughEx->TimeOutValue == 0 ||
|
|
ataPassThroughEx->TimeOutValue > 30 * 60 * 60) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PSCSI_REQUEST_BLOCK
|
|
IdeAtaPassThroughSetupSrb (
|
|
PPDO_EXTENSION PdoExtension,
|
|
PVOID DataBuffer,
|
|
ULONG DataBufferLength,
|
|
ULONG TimeOutValue,
|
|
ULONG AtaFlags,
|
|
PUCHAR CurrentTaskFile,
|
|
PUCHAR PreviousTaskFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds an SRB for ATA PASS THROUGH.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension : The pdo which the request is destined for
|
|
DataBuffer : Pointer to the data buffer.
|
|
DataBufferLength : The size of the data buffer
|
|
TimeOutValue: The request timeout value
|
|
AtaFlags : Specifies the flags for the request
|
|
CurrentTaskFile : the current ata registers
|
|
PreviousTaskFile: previous values for 48 bit LBA feature set
|
|
|
|
Return Value:
|
|
|
|
Pointer to an SRB if one was allocated successfully, NULL otherwise.
|
|
|
|
--*/
|
|
{
|
|
PSCSI_REQUEST_BLOCK srb = NULL;
|
|
PIDEREGS pIdeReg;
|
|
|
|
//
|
|
// allocate the srb
|
|
//
|
|
srb = ExAllocatePool (NonPagedPool, sizeof (SCSI_REQUEST_BLOCK));
|
|
|
|
if (srb == NULL) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Fill in the srb.
|
|
//
|
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
srb->Function = SRB_FUNCTION_ATA_PASS_THROUGH_EX;
|
|
srb->SrbStatus = SRB_STATUS_PENDING;
|
|
srb->PathId = PdoExtension->PathId;
|
|
srb->TargetId = PdoExtension->TargetId;
|
|
srb->Lun = PdoExtension->Lun;
|
|
srb->SenseInfoBufferLength = 0;
|
|
srb->TimeOutValue = TimeOutValue;
|
|
|
|
|
|
if (DataBufferLength != 0) {
|
|
|
|
if (AtaFlags & ATA_FLAGS_DATA_IN) {
|
|
srb->SrbFlags |= SRB_FLAGS_DATA_IN;
|
|
}
|
|
|
|
if (AtaFlags & ATA_FLAGS_DATA_OUT) {
|
|
srb->SrbFlags |= SRB_FLAGS_DATA_OUT;
|
|
}
|
|
}
|
|
|
|
srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE;
|
|
srb->SrbFlags |= SRB_FLAGS_NO_QUEUE_FREEZE;
|
|
srb->DataTransferLength = DataBufferLength;
|
|
srb->DataBuffer = DataBuffer;
|
|
srb->SenseInfoBuffer = NULL;
|
|
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
RtlCopyMemory(srb->Cdb,
|
|
CurrentTaskFile,
|
|
8
|
|
);
|
|
|
|
RtlCopyMemory((PUCHAR) (&srb->Cdb[8]),
|
|
PreviousTaskFile,
|
|
8
|
|
);
|
|
|
|
pIdeReg = (PIDEREGS) (srb->Cdb);
|
|
|
|
if (AtaFlags & ATA_FLAGS_DRDY_REQUIRED) {
|
|
|
|
pIdeReg->bReserved |= ATA_PTFLAGS_STATUS_DRDY_REQUIRED;
|
|
}
|
|
|
|
return srb;
|
|
}
|
|
|
|
PIRP
|
|
IdeAtaPassThroughSetupIrp (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVOID DataBuffer,
|
|
ULONG DataBufferLength,
|
|
KPROCESSOR_MODE AccessMode,
|
|
BOOLEAN DataIn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds an Irp to handle ata pass through.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject : The Pdo.
|
|
DataBuffer : Pointer to the data buffer.
|
|
DataBufferLength: its size
|
|
Access Mode: KernelMode or UserMode
|
|
DataIn: indicates the direction of transfer
|
|
|
|
Return Value:
|
|
|
|
PIRP if one was allocated successfully, NULL otherwise.
|
|
|
|
--*/
|
|
{
|
|
PIRP irp = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// allocate the irp
|
|
//
|
|
irp = IoAllocateIrp (
|
|
(CCHAR) (DeviceObject->StackSize),
|
|
FALSE
|
|
);
|
|
|
|
if (irp == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// allocate the mdl if needed
|
|
//
|
|
if (DataBufferLength != 0) {
|
|
|
|
ASSERT(irp);
|
|
|
|
irp->MdlAddress = IoAllocateMdl( DataBuffer,
|
|
DataBufferLength,
|
|
FALSE,
|
|
FALSE,
|
|
(PIRP) NULL
|
|
);
|
|
|
|
if (irp->MdlAddress == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
//
|
|
// lock the pages
|
|
//
|
|
try {
|
|
|
|
MmProbeAndLockPages( irp->MdlAddress,
|
|
AccessMode,
|
|
(LOCK_OPERATION) (DataIn ? IoWriteAccess : IoReadAccess)
|
|
);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if (irp->MdlAddress != NULL) {
|
|
|
|
IoFreeMdl( irp->MdlAddress );
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
}
|
|
|
|
if (irp->MdlAddress == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Flush the data buffer for output. This will insure that
|
|
// the data is written back to memory.
|
|
//
|
|
KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
GetOut:
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
if (irp) {
|
|
|
|
if (irp->MdlAddress) {
|
|
|
|
//
|
|
// if mdladdress is set then the probeandlock
|
|
// succeeded. So unlock it now.
|
|
//
|
|
MmUnlockPages(irp->MdlAddress);
|
|
|
|
IoFreeMdl( irp->MdlAddress );
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
|
|
IoFreeIrp(irp);
|
|
irp = NULL;
|
|
}
|
|
}
|
|
|
|
return irp;
|
|
}
|
|
|
|
VOID
|
|
IdeAtaPassThroughFreeIrp (
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the irp and mdl allocated by IdeAtaPassThroughSetupIrp
|
|
|
|
Arguments:
|
|
|
|
Irp: Irp to be freed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(Irp);
|
|
|
|
if (Irp->MdlAddress) {
|
|
MmUnlockPages(Irp->MdlAddress);
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
}
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
IdeAtaPassThroughFreeSrb (
|
|
PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the srb allocated by IdeAtaPassThroughSetupSrb
|
|
|
|
Arguments:
|
|
|
|
Srb: The srb to be freed.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ASSERT(Srb);
|
|
|
|
ExFreePool(Srb);
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughSyncCompletion (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The completion routine for IdeAtaPassThroughSendSynchronous. It
|
|
just signals the event.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject : Not used.
|
|
Irp : Not Used
|
|
Context : Event to be signalled
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED always.
|
|
|
|
--*/
|
|
{
|
|
PKEVENT event = Context;
|
|
|
|
KeSetEvent (event, 0, FALSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeAtaPassThroughSendSynchronous (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends the irp synchronously to the PDO
|
|
|
|
Arguments:
|
|
|
|
DeviceObject : The pdo
|
|
|
|
Return Value:
|
|
|
|
The irp's status
|
|
|
|
--*/
|
|
{
|
|
KEVENT event;
|
|
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
IoSetCompletionRoutine (Irp,
|
|
IdeAtaPassThroughSyncCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
IoCallDriver(DeviceObject, Irp);
|
|
|
|
KeWaitForSingleObject (&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
VOID
|
|
IdeAtaPassThroughMarshalResults(
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PATA_PASS_THROUGH_EX AtaPassThroughEx,
|
|
IN BOOLEAN Direct,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fills in the IoStatus block with the appropriate status and information
|
|
length. It also updates certain fields in the atapassthroughEx structure.
|
|
|
|
Arguments:
|
|
|
|
Srb: The pass through srb.
|
|
AtaPassThroughEx : the pass through structure.
|
|
Direct : True if it is a direct ioctl.
|
|
IoStatus : The Io status block that needs to be filled in.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// copy over the task file registers
|
|
//
|
|
RtlCopyMemory(AtaPassThroughEx->CurrentTaskFile,
|
|
Srb->Cdb,
|
|
8
|
|
);
|
|
|
|
RtlCopyMemory(AtaPassThroughEx->PreviousTaskFile,
|
|
(PUCHAR) (&Srb->Cdb[8]),
|
|
8
|
|
);
|
|
|
|
//
|
|
// zero out the reserved register as it is used by the
|
|
// port driver
|
|
//
|
|
AtaPassThroughEx->CurrentTaskFile[7] = 0;
|
|
AtaPassThroughEx->PreviousTaskFile[7] = 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) {
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set the information length
|
|
//
|
|
AtaPassThroughEx->DataTransferLength = Srb->DataTransferLength;
|
|
|
|
if (Direct == TRUE) {
|
|
|
|
//
|
|
// the data is transferred directly to the supplied data buffer
|
|
//
|
|
IoStatus->Information = AtaPassThroughEx->Length;
|
|
|
|
} else {
|
|
|
|
//
|
|
// actual data is returned
|
|
//
|
|
if (DataIn(AtaPassThroughEx) &&
|
|
AtaPassThroughEx->DataBufferOffset != 0) {
|
|
|
|
IoStatus->Information =
|
|
AtaPassThroughEx->DataBufferOffset +
|
|
AtaPassThroughEx->DataTransferLength;
|
|
|
|
} else {
|
|
|
|
IoStatus->Information = AtaPassThroughEx->Length;
|
|
}
|
|
}
|
|
|
|
ASSERT((Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) == 0);
|
|
|
|
return;
|
|
}
|
|
|
|
#if defined (_WIN64)
|
|
|
|
VOID
|
|
IdeTranslateAtaPassThrough32To64(
|
|
IN PATA_PASS_THROUGH_EX32 AtaPassThrough32,
|
|
IN OUT PATA_PASS_THROUGH_EX AtaPassThrough64
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs that marshaling.
|
|
|
|
Arguments:
|
|
|
|
ataPassThrough32 - Supplies a pointer to a 32-bit ATA_PASS_THROUGH
|
|
struct.
|
|
|
|
ataPassThrough64 - Supplies a pointer to a 64-bit ATA_PASS_THROUGH
|
|
structure, into which we'll copy the marshaled
|
|
32-bit data.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
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.
|
|
//
|
|
|
|
RtlCopyMemory(AtaPassThrough64,
|
|
AtaPassThrough32,
|
|
FIELD_OFFSET(ATA_PASS_THROUGH_EX, DataBufferOffset)
|
|
);
|
|
|
|
//
|
|
// Copy over the Taskfile.
|
|
//
|
|
|
|
RtlCopyMemory(AtaPassThrough64->CurrentTaskFile,
|
|
AtaPassThrough32->CurrentTaskFile,
|
|
8 * sizeof(UCHAR)
|
|
);
|
|
|
|
RtlCopyMemory(AtaPassThrough64->PreviousTaskFile,
|
|
AtaPassThrough32->PreviousTaskFile,
|
|
8 * sizeof(UCHAR)
|
|
);
|
|
|
|
//
|
|
// Copy the fields that follow the ULONG_PTR.
|
|
//
|
|
|
|
AtaPassThrough64->DataBufferOffset =
|
|
(ULONG_PTR)AtaPassThrough32->DataBufferOffset;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
IdeTranslateAtaPassThrough64To32(
|
|
IN PATA_PASS_THROUGH_EX AtaPassThrough64,
|
|
IN OUT PATA_PASS_THROUGH_EX32 AtaPassThrough32
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function marshals a 64-bit version of the structure back into a
|
|
32-bit version.
|
|
|
|
Arguments:
|
|
|
|
atapassthrough64 - Supplies a pointer to a 64-bit ATA_PASS_THROUGH
|
|
struct.
|
|
|
|
ataPassThrough32 - Supplies the address of a pointer to a 32-bit
|
|
ATA_PASS_THROUGH structure, into which we'll copy the
|
|
marshaled 64-bit data.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy back the fields through the data offsets.
|
|
//
|
|
|
|
RtlCopyMemory(AtaPassThrough32,
|
|
AtaPassThrough64,
|
|
FIELD_OFFSET(ATA_PASS_THROUGH_EX, DataBufferOffset));
|
|
|
|
//
|
|
// copy over the task file
|
|
//
|
|
RtlCopyMemory(AtaPassThrough32->CurrentTaskFile,
|
|
AtaPassThrough64->CurrentTaskFile,
|
|
8 * sizeof(UCHAR)
|
|
);
|
|
|
|
RtlCopyMemory(AtaPassThrough32->PreviousTaskFile,
|
|
AtaPassThrough64->PreviousTaskFile,
|
|
8 * sizeof(UCHAR)
|
|
);
|
|
|
|
return;
|
|
}
|
|
#endif
|