mirror of
https://github.com/reactos/reactos.git
synced 2026-05-24 16:20:07 +08:00
658 lines
20 KiB
C
658 lines
20 KiB
C
/*
|
|
* PROJECT: ReactOS ATA Port Driver
|
|
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
|
* PURPOSE: Device I/O error handling
|
|
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "atapi.h"
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaDeviceRequestSenseNeeded(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
return ((Request->Flags & REQUEST_FLAG_PACKET_COMMAND) &&
|
|
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(Request->Srb->SenseInfoBuffer != NULL) &&
|
|
(Request->Srb->SenseInfoBufferLength != 0));
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaDeviceRequestSenseNeededExt(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
/* NOTE: The ERROR bit and the SENSE DATA AVAILABLE bit may both be set to one */
|
|
return ((DevExt->Device.DeviceFlags & DEVICE_SENSE_DATA_REPORTING) &&
|
|
(Request->Output.Status & IDE_STATUS_INDEX) &&
|
|
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(Request->Srb->SenseInfoBuffer != NULL) &&
|
|
(Request->Srb->SenseInfoBufferLength != 0));
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaDeviceFixedErrorNeeded(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
return (!(Request->Flags & REQUEST_FLAG_PACKET_COMMAND) &&
|
|
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
|
(Request->Srb->SenseInfoBuffer != NULL) &&
|
|
(Request->Srb->SenseInfoBufferLength != 0));
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaDeviceIsDmaCrcError(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
// TODO: Check also for SATA SErr or AHCI interrupt status bits
|
|
return (Request->Output.Error & IDE_ERROR_CRC_ERROR) &&
|
|
(Request->Flags & REQUEST_FLAG_DMA);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaDeviceHandleRequestSense(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
|
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
|
|
PSCSI_REQUEST_BLOCK Srb = FailedRequest->Srb;
|
|
PSENSE_DATA SenseData;
|
|
BOOLEAN Success;
|
|
|
|
if (IS_ATAPI(&DevExt->Device))
|
|
{
|
|
Success = (Request->SrbStatus == SRB_STATUS_SUCCESS) ||
|
|
(Request->SrbStatus == SRB_STATUS_DATA_OVERRUN);
|
|
}
|
|
else
|
|
{
|
|
Success = (Request->SrbStatus == SRB_STATUS_SUCCESS) &&
|
|
(Request->Flags & REQUEST_FLAG_HAS_TASK_FILE) &&
|
|
(Request->TaskFile.HighLba != 0);
|
|
}
|
|
|
|
if (!Success)
|
|
{
|
|
ERR("Request sense failed\n");
|
|
|
|
if (IS_ATAPI(&DevExt->Device))
|
|
{
|
|
Srb->ScsiStatus = SCSISTAT_GOOD;
|
|
FailedRequest->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED;
|
|
}
|
|
else
|
|
{
|
|
/* Request sense failed, translate the original ATA device error */
|
|
FailedRequest->SrbStatus = AtaReqSetFixedAtaSenseData(FailedRequest);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
SenseData = Srb->SenseInfoBuffer;
|
|
|
|
if (IS_ATAPI(&DevExt->Device))
|
|
{
|
|
/* Copy the sense data from the local buffer */
|
|
RtlCopyMemory(Srb->SenseInfoBuffer,
|
|
DevExt->Device.LocalBuffer,
|
|
min(Srb->SenseInfoBufferLength, ATA_LOCAL_BUFFER_SIZE));
|
|
|
|
Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
|
FailedRequest->SrbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
|
|
}
|
|
else
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
/* Copy the sense code from the task file registers */
|
|
SenseCode.SrbStatus = SRB_STATUS_ERROR;
|
|
SenseCode.SenseKey = Request->TaskFile.HighLba;
|
|
SenseCode.AdditionalSenseCode = Request->TaskFile.MidLba;
|
|
SenseCode.AdditionalSenseCodeQualifier = Request->TaskFile.LowLba;
|
|
FailedRequest->SrbStatus = AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
}
|
|
|
|
if (RTL_CONTAINS_FIELD(SenseData,
|
|
Srb->SenseInfoBufferLength,
|
|
AdditionalSenseCodeQualifier))
|
|
{
|
|
/* INFO("0x%02X: SK 0x%02X, ASC 0x%02X, ASCQ 0x%02X\n", */
|
|
/* Srb->Cdb[0], */
|
|
/* SenseData->SenseKey, */
|
|
/* SenseData->AdditionalSenseCode, */
|
|
/* SenseData->AdditionalSenseCodeQualifier); */
|
|
}
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
AtaDeviceSendRequestSense(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
|
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
|
|
NTSTATUS Status;
|
|
PCDB Cdb;
|
|
|
|
ASSERT_REQUEST(FailedRequest);
|
|
ASSERT(FailedRequest->Srb->SenseInfoBufferLength > 0);
|
|
|
|
Request->Flags = REQUEST_FLAG_DATA_IN |
|
|
REQUEST_FLAG_PACKET_COMMAND |
|
|
REQUEST_FLAG_HAS_LOCAL_BUFFER;
|
|
Request->TimeOut = 3;
|
|
Request->DataTransferLength = FailedRequest->Srb->SenseInfoBufferLength;
|
|
|
|
Cdb = (PCDB)Request->Cdb;
|
|
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
|
|
Cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
|
|
Cdb->CDB6INQUIRY.Reserved1 = 0;
|
|
Cdb->CDB6INQUIRY.PageCode = 0;
|
|
Cdb->CDB6INQUIRY.IReserved = 0;
|
|
Cdb->CDB6INQUIRY.AllocationLength = (UCHAR)Request->DataTransferLength;
|
|
Cdb->CDB6INQUIRY.Control = 0;
|
|
|
|
Status = AtaPortSendRequest(PortData, DevExt);
|
|
|
|
AtaDeviceHandleRequestSense(PortData, DevExt);
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
AtaDeviceSendRequestSenseExt(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
|
NTSTATUS Status;
|
|
|
|
Request->Flags = REQUEST_FLAG_SAVE_TASK_FILE | REQUEST_FLAG_LBA48;
|
|
Request->TimeOut = 3;
|
|
|
|
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
|
Request->TaskFile.Command = IDE_COMMAND_REQUEST_SENSE_DATA_EXT;
|
|
|
|
Status = AtaPortSendRequest(PortData, DevExt);
|
|
|
|
AtaDeviceHandleRequestSense(PortData, DevExt);
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
AtaDeviceSendReadNcqCommandErrorLog(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
|
|
|
AtaReqBuildReadLogTaskFile(Request,
|
|
IDE_GP_LOG_NCQ_COMMAND_ERROR_ADDRESS,
|
|
0,
|
|
1);
|
|
Request->Flags |= REQUEST_FLAG_HAS_LOCAL_BUFFER;
|
|
Request->TimeOut = 3;
|
|
|
|
return AtaPortSendRequest(PortData, DevExt);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaDeviceSaveLogPageTaskFile(
|
|
_In_ GP_LOG_NCQ_COMMAND_ERROR* __restrict LogPage,
|
|
_Inout_ ATA_DEVICE_REQUEST* __restrict Request)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->Output;
|
|
|
|
TaskFile->SectorCount = LogPage->Count7_0;
|
|
TaskFile->LowLba = LogPage->LBA7_0;
|
|
TaskFile->MidLba = LogPage->LBA15_8;
|
|
TaskFile->HighLba = LogPage->LBA23_16;
|
|
TaskFile->DriveSelect = LogPage->Device;
|
|
|
|
if (Request->Flags & REQUEST_FLAG_LBA48)
|
|
{
|
|
TaskFile->FeatureEx = 0; // Reserved
|
|
TaskFile->SectorCountEx = LogPage->Count15_8;
|
|
TaskFile->LowLbaEx = LogPage->LBA31_24;
|
|
TaskFile->MidLbaEx = LogPage->LBA39_32;
|
|
TaskFile->HighLbaEx = LogPage->LBA47_40;
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaDeviceLogEvent(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ ATA_ERROR_LOG_VALUE ErrorValue)
|
|
{
|
|
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
|
ULONG ErrorCode, FinalStatus;
|
|
PIO_ERROR_LOG_PACKET LogEntry;
|
|
UCHAR Size;
|
|
|
|
Size = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
|
|
LogEntry = IoAllocateErrorLogEntry(ChanExt->Common.Self, Size);
|
|
if (!LogEntry)
|
|
return;
|
|
|
|
switch (ErrorValue)
|
|
{
|
|
case EVENT_CODE_TIMEOUT:
|
|
ErrorCode = IO_ERR_TIMEOUT;
|
|
FinalStatus = STATUS_IO_TIMEOUT;
|
|
break;
|
|
case EVENT_CODE_CRC_ERROR:
|
|
ErrorCode = IO_ERR_PARITY;
|
|
FinalStatus = STATUS_IO_DEVICE_ERROR;
|
|
break;
|
|
|
|
default:
|
|
ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
FinalStatus = STATUS_IO_DEVICE_ERROR;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(LogEntry, Size);
|
|
LogEntry->FinalStatus = FinalStatus;
|
|
LogEntry->ErrorCode = ErrorCode;
|
|
LogEntry->MajorFunctionCode = IRP_MJ_SCSI;
|
|
LogEntry->UniqueErrorValue = ErrorValue;
|
|
|
|
IoWriteErrorLogEntry(LogEntry);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaDeviceDowngradeTransferSpeed(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
ULONG Mode, AllowedModesMask;
|
|
|
|
/* Already in PIO mode */
|
|
if (!_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & ~PIO_ALL))
|
|
return;
|
|
|
|
/* Clear the current mode and the upper bits */
|
|
AllowedModesMask = ((1 << Mode) - 1);
|
|
|
|
if ((DevExt->TransferModeSupportedBitmap & UDMA_ALL) && !(AllowedModesMask & UDMA_ALL))
|
|
{
|
|
/*
|
|
* It makes no sense to disable DMA for AHCI hard drives
|
|
* because that would otherwise cause READ/WRITE commands to fail.
|
|
*/
|
|
if ((PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA) && !IS_ATAPI(&DevExt->Device))
|
|
return;
|
|
|
|
/* From UDMA to PIO (intentionally skip MWDMA and SWDMA) */
|
|
AllowedModesMask &= PIO_ALL;
|
|
}
|
|
else if (((DevExt->TransferModeSupportedBitmap & MWDMA_ALL) &&
|
|
!(AllowedModesMask & MWDMA_ALL)) ||
|
|
((DevExt->TransferModeSupportedBitmap & SWDMA_ALL) &&
|
|
!(AllowedModesMask & SWDMA_ALL)))
|
|
{
|
|
/* From MWDMA to PIO (intentionally skip SWDMA) or from SWDMA to PIO */
|
|
AllowedModesMask &= PIO_ALL;
|
|
}
|
|
|
|
/* We are about to disable DMA, log the change */
|
|
if ((DevExt->TransferModeSupportedBitmap & ~PIO_ALL) && !(AllowedModesMask & ~PIO_ALL))
|
|
{
|
|
WARN("Too many DMA failures, disabling DMA for '%s'\n", DevExt->FriendlyName);
|
|
|
|
AtaDeviceLogEvent(DevExt, EVENT_CODE_DMA_DISABLE);
|
|
}
|
|
|
|
WARN("Downgrading DMA speed from %lu for '%s'\n", Mode, DevExt->FriendlyName);
|
|
|
|
DevExt->TransferModeAllowedMask &= AllowedModesMask;
|
|
|
|
/* Program the new timings */
|
|
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_PORT_TIMING | ACTION_DEVICE_CONFIG);
|
|
_InterlockedOr(&DevExt->Worker.EventsPending, ACTION_DEVICE_CONFIG);
|
|
|
|
/* Request a QBR to ensure that storprop.dll updates its data from the registry */
|
|
PortData->Worker.Flags |= WORKER_FLAG_NEED_RESCAN;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaDeviceAnalyzeDmaError(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ ATA_ERROR_LOG_VALUE ErrorValue)
|
|
{
|
|
LARGE_INTEGER CurrentTime, TimeDifferenceMs;
|
|
|
|
WARN("DMA error %lu on '%s'\n", ErrorValue, DevExt->FriendlyName);
|
|
|
|
/* We silently ignore device errors caused by PASSTHROUGH commands came from user mode */
|
|
if (Request->Flags & REQUEST_FLAG_PASSTHROUGH)
|
|
return;
|
|
|
|
KeQuerySystemTime(&CurrentTime);
|
|
TimeDifferenceMs.QuadPart = (CurrentTime.QuadPart - DevExt->LastDmaErrorTime.QuadPart) / 10000;
|
|
DevExt->LastDmaErrorTime.QuadPart = CurrentTime.QuadPart;
|
|
|
|
AtaDeviceLogEvent(DevExt, ErrorValue);
|
|
|
|
/* Ignore all occasional DMA errors we encounter */
|
|
if (TimeDifferenceMs.QuadPart >= (10LL * 60000LL)) // 10 min
|
|
return;
|
|
|
|
/* DMA timeouts and DMA CRC errors usually indicate a bad connection (a bad cable) */
|
|
|
|
/* Try to disable NCQ */
|
|
if (DevExt->Device.DeviceFlags & DEVICE_NCQ)
|
|
{
|
|
DevExt->Device.DeviceFlags &= ~DEVICE_NCQ;
|
|
|
|
ERR("NCQ disabled for '%s'\n", DevExt->FriendlyName);
|
|
AtaDeviceLogEvent(DevExt, EVENT_CODE_NCQ_DISABLE);
|
|
return;
|
|
}
|
|
|
|
/* Try to reduce the interface speed */
|
|
if (PortData->DowngradeInterfaceSpeed(PortData->ChannelContext))
|
|
{
|
|
WARN("Downgrading interface for '%s'\n", DevExt->FriendlyName);
|
|
AtaDeviceLogEvent(DevExt, EVENT_CODE_DOWNSHIFT);
|
|
return;
|
|
}
|
|
|
|
/* Try to reduce the transfer speed */
|
|
AtaDeviceDowngradeTransferSpeed(PortData, DevExt);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaDeviceCompleteFailedRequest(
|
|
_In_ PATAPORT_PORT_DATA PortData)
|
|
{
|
|
PATA_DEVICE_REQUEST Request = PortData->Worker.FailedRequest;
|
|
UCHAR SrbStatus;
|
|
SIZE_T RetryCount;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT_REQUEST(Request);
|
|
ASSERT(Request != &PortData->Worker.InternalRequest);
|
|
|
|
SrbStatus = SRB_STATUS(Request->SrbStatus);
|
|
|
|
RetryCount = SRB_GET_FLAGS(Request->Srb) & SRB_FLAG_RETRY_COUNT_MASK;
|
|
RetryCount++;
|
|
SRB_CLEAR_FLAGS(Request->Srb, SRB_FLAG_RETRY_COUNT_MASK);
|
|
SRB_SET_FLAGS(Request->Srb, RetryCount);
|
|
|
|
if (RetryCount > 3)
|
|
{
|
|
ASSERT(SrbStatus != SRB_STATUS_BUSY);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We usually do not want to retry a request, because the upper class driver
|
|
* is intended to effectively analyze errors and sense data.
|
|
*/
|
|
switch (SrbStatus)
|
|
{
|
|
case SRB_STATUS_BUS_RESET:
|
|
case SRB_STATUS_TIMEOUT:
|
|
{
|
|
/* DMA timeout, attempt to retry in PIO mode */
|
|
if (!(PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA))
|
|
{
|
|
SRB_SET_FLAGS(Request->Srb, SRB_FLAG_PIO_RETRY);
|
|
}
|
|
|
|
SrbStatus = SRB_STATUS_BUSY;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_REQUEST_SENSE_FAILED:
|
|
{
|
|
SrbStatus = SRB_STATUS_BUSY;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* Retry paging I/O operations only */
|
|
if (Request->Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO))
|
|
SrbStatus = SRB_STATUS_BUSY;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SrbStatus |= Request->SrbStatus & (SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN);
|
|
Request->SrbStatus = SrbStatus;
|
|
|
|
if (SRB_STATUS(SrbStatus) == SRB_STATUS_BUSY)
|
|
return;
|
|
|
|
ASSERT(SRB_STATUS(SrbStatus) != SRB_STATUS_SUCCESS);
|
|
|
|
/* Fail the command with an error */
|
|
Request->InternalState = REQUEST_STATE_FREEZE_QUEUE;
|
|
|
|
OldIrql = KeAcquireInterruptSpinLock(PortData->InterruptObject);
|
|
|
|
ASSERT(PortData->Worker.PausedSlotsBitmap & (1 << Request->Slot));
|
|
PortData->Worker.PausedSlotsBitmap &= ~(1 << Request->Slot);
|
|
|
|
KeReleaseInterruptSpinLock(PortData->InterruptObject, OldIrql);
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
AtaDeviceNcqRecovery(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST FailedRequest;
|
|
PGP_LOG_NCQ_COMMAND_ERROR LogPage;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
UCHAR Crc;
|
|
|
|
Status = AtaDeviceSendReadNcqCommandErrorLog(PortData, DevExt);
|
|
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
|
return Status;
|
|
/* We failed at retrieving the log page */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("READ LOG EXT failure\n");
|
|
goto Failure;
|
|
}
|
|
|
|
LogPage = DevExt->Device.LocalBuffer;
|
|
|
|
/* It's not ours */
|
|
if (LogPage->NonQueuedCmd)
|
|
{
|
|
ERR("Unexpected NQ bit in the log page 0x10 structure\n");
|
|
goto Failure;
|
|
}
|
|
|
|
/* Verify the checksum */
|
|
Crc = 0;
|
|
for (i = 0; i < IDE_GP_LOG_SECTOR_SIZE; ++i)
|
|
{
|
|
Crc += ((PUCHAR)LogPage)[i];
|
|
}
|
|
if (Crc != 0)
|
|
{
|
|
ERR("CRC error in the log page 0x10 structure\n");
|
|
goto Failure;
|
|
}
|
|
|
|
/* Find the failed queued command */
|
|
if (!(PortData->Worker.PausedSlotsBitmap & (1 << LogPage->NcqTag)))
|
|
{
|
|
ERR("Failed command %08lx not found in %08lx\n",
|
|
1 << LogPage->NcqTag, PortData->Worker.PausedSlotsBitmap);
|
|
goto Failure;
|
|
}
|
|
|
|
FailedRequest = PortData->Slots[LogPage->NcqTag];
|
|
ASSERT_REQUEST(FailedRequest);
|
|
PortData->Worker.FailedRequest = FailedRequest;
|
|
|
|
/* Fail the command with an error */
|
|
FailedRequest->SrbStatus = SRB_STATUS_ERROR;
|
|
FailedRequest->Output.Status = LogPage->Status;
|
|
FailedRequest->Output.Error = LogPage->Error;
|
|
if (FailedRequest->Flags & REQUEST_FLAG_SAVE_TASK_FILE)
|
|
{
|
|
AtaDeviceSaveLogPageTaskFile(LogPage, FailedRequest);
|
|
}
|
|
|
|
if ((LogPage->SenseKey != 0) && AtaDevHasNcqAutosense(&DevExt->IdentifyDeviceData))
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
/* Set the sense data returned by the device */
|
|
SenseCode.SrbStatus = FailedRequest->SrbStatus;
|
|
SenseCode.SenseKey = LogPage->SenseKey;
|
|
SenseCode.AdditionalSenseCode = LogPage->ASC;
|
|
SenseCode.AdditionalSenseCodeQualifier = LogPage->ASCQ;
|
|
FailedRequest->SrbStatus = AtaReqSetFixedSenseData(FailedRequest->Srb, SenseCode);
|
|
|
|
/* Set the "Final LBA in Error" field */
|
|
AtaReqSetLbaInformation(FailedRequest->Srb,
|
|
((ULONG64)(((PUCHAR)LogPage)[17]) << 0) |
|
|
((ULONG64)(((PUCHAR)LogPage)[18]) << 8) |
|
|
((ULONG64)(((PUCHAR)LogPage)[19]) << 16) |
|
|
((ULONG64)(((PUCHAR)LogPage)[20]) << 24) |
|
|
((ULONG64)(((PUCHAR)LogPage)[21]) << 32) |
|
|
((ULONG64)(((PUCHAR)LogPage)[22]) << 48));
|
|
}
|
|
else if (AtaDeviceRequestSenseNeededExt(DevExt, FailedRequest))
|
|
{
|
|
/* Get sense data */
|
|
Status = AtaDeviceSendRequestSenseExt(PortData, DevExt);
|
|
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
|
return Status;
|
|
}
|
|
else
|
|
{
|
|
FailedRequest->SrbStatus = AtaReqSetFixedAtaSenseData(FailedRequest);
|
|
}
|
|
|
|
AtaDeviceCompleteFailedRequest(PortData);
|
|
return STATUS_SUCCESS;
|
|
|
|
Failure:
|
|
/* A port reset is required to abort all outstanding queued commands */
|
|
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_PORT_RESET);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
AtaDeviceGenericRecovery(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST Request = PortData->Worker.FailedRequest;
|
|
NTSTATUS Status;
|
|
|
|
switch (Request->SrbStatus)
|
|
{
|
|
/* Unexpected channel/device state */
|
|
case SRB_STATUS_BUS_RESET:
|
|
case SRB_STATUS_TIMEOUT:
|
|
{
|
|
if (Request->Flags & REQUEST_FLAG_DMA)
|
|
{
|
|
ATA_ERROR_LOG_VALUE ErrorValue;
|
|
|
|
if (Request->SrbStatus == SRB_STATUS_TIMEOUT)
|
|
ErrorValue = EVENT_CODE_TIMEOUT;
|
|
else
|
|
ErrorValue = EVENT_CODE_BAD_STATE;
|
|
AtaDeviceAnalyzeDmaError(PortData, DevExt, Request, ErrorValue);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* General errors */
|
|
case SRB_STATUS_ERROR:
|
|
{
|
|
if (AtaDeviceIsDmaCrcError(DevExt, Request))
|
|
{
|
|
AtaDeviceAnalyzeDmaError(PortData, DevExt, Request, EVENT_CODE_CRC_ERROR);
|
|
break;
|
|
}
|
|
|
|
/* Send the recovery command to figure out why the current command failed */
|
|
if (AtaDeviceRequestSenseNeeded(Request))
|
|
{
|
|
/* Handle failed ATAPI commands */
|
|
Status = AtaDeviceSendRequestSense(PortData, DevExt);
|
|
break;
|
|
}
|
|
else if (AtaDeviceRequestSenseNeededExt(DevExt, Request))
|
|
{
|
|
/* Handle failed non-queued commands */
|
|
Status = AtaDeviceSendRequestSenseExt(PortData, DevExt);
|
|
break;
|
|
}
|
|
else if (AtaDeviceFixedErrorNeeded(DevExt, Request))
|
|
{
|
|
Request->SrbStatus = AtaReqSetFixedAtaSenseData(Request);
|
|
}
|
|
|
|
/* Recovery command is not required, just complete the request with an error */
|
|
__fallthrough;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
|
return Status;
|
|
|
|
AtaDeviceCompleteFailedRequest(PortData);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AtaPortDeviceProcessError(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT_REQUEST(FailedRequest);
|
|
|
|
if (FailedRequest->Flags & REQUEST_FLAG_NCQ)
|
|
Status = AtaDeviceNcqRecovery(PortData, DevExt);
|
|
else
|
|
Status = AtaDeviceGenericRecovery(PortData, DevExt);
|
|
|
|
return Status;
|
|
}
|