summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c71
-rw-r--r--MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h3
2 files changed, 69 insertions, 5 deletions
diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
index 06c4a3e052..c0c8ffbd9e 100644
--- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
+++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
@@ -737,6 +737,7 @@ AhciRecoverPortError (
Status = AhciResetPort (PciIo, Port);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed to reset the port %d\n", Port));
+ return EFI_DEVICE_ERROR;
}
}
@@ -744,6 +745,61 @@ AhciRecoverPortError (
}
/**
+ This function will check if the failed command should be retired. Only error
+ conditions which are a result of transient conditions on a link(either to system or to device).
+
+ @param[in] PciIo Pointer to AHCI controller PciIo.
+ @param[in] Port SATA port index on which to check.
+
+ @retval TRUE Command failure was caused by transient condition and should be retried
+ @retval FALSE Command should not be retried
+**/
+BOOLEAN
+AhciShouldCmdBeRetried (
+ IN EFI_PCI_IO_PROTOCOL *PciIo,
+ IN UINT8 Port
+ )
+{
+ UINT32 Offset;
+ UINT32 PortInterrupt;
+ UINT32 Serr;
+ UINT32 Tfd;
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
+ PortInterrupt = AhciReadReg (PciIo, Offset);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
+ Serr = AhciReadReg (PciIo, Offset);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
+ Tfd = AhciReadReg (PciIo, Offset);
+
+ //
+ // This can occur if there was a CRC error on a path from system memory to
+ // host controller.
+ //
+ if (PortInterrupt & EFI_AHCI_PORT_IS_HBDS) {
+ return TRUE;
+ //
+ // This can occur if there was a CRC error detected by host during communication
+ // with the device
+ //
+ } else if ((PortInterrupt & (EFI_AHCI_PORT_IS_IFS | EFI_AHCI_PORT_IS_INFS)) &&
+ (Serr & EFI_AHCI_PORT_SERR_CRCE))
+ {
+ return TRUE;
+ //
+ // This can occur if there was a CRC error detected by device during communication
+ // with the host. Device returns error status to host with D2H FIS.
+ //
+ } else if ((PortInterrupt & EFI_AHCI_PORT_IS_TFES) &&
+ (Tfd & EFI_AHCI_PORT_TFD_ERR_INT_CRC))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
Checks if specified FIS has been received.
@param[in] PciIo Pointer to AHCI controller PciIo.
@@ -950,6 +1006,7 @@ AhciPioTransfer (
UINT32 PrdCount;
UINT32 Retry;
EFI_STATUS RecoveryStatus;
+ BOOLEAN DoRetry;
if (Read) {
Flag = EfiPciIoOperationBusMasterWrite;
@@ -1027,8 +1084,9 @@ AhciPioTransfer (
if (Status == EFI_DEVICE_ERROR) {
DEBUG ((DEBUG_ERROR, "PIO command failed at retry %d\n", Retry));
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // needs to be called before error recovery
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
- if (EFI_ERROR (RecoveryStatus)) {
+ if (!DoRetry || EFI_ERROR (RecoveryStatus)) {
break;
}
} else {
@@ -1124,6 +1182,7 @@ AhciDmaTransfer (
EFI_TPL OldTpl;
UINT32 Retry;
EFI_STATUS RecoveryStatus;
+ BOOLEAN DoRetry;
Map = NULL;
PciIo = Instance->PciIo;
@@ -1222,8 +1281,9 @@ AhciDmaTransfer (
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
if (Status == EFI_DEVICE_ERROR) {
DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Retry));
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // needs to be called before error recovery
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
- if (EFI_ERROR (RecoveryStatus)) {
+ if (!DoRetry || EFI_ERROR (RecoveryStatus)) {
break;
}
} else {
@@ -1263,6 +1323,7 @@ AhciDmaTransfer (
Status = AhciCheckFisReceived (PciIo, Port, SataFisD2H);
if (Status == EFI_DEVICE_ERROR) {
DEBUG ((DEBUG_ERROR, "DMA command failed at retry: %d\n", Task->RetryTimes));
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // call this before error recovery
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
//
// If recovery passed mark the Task as not started and change the status
@@ -1270,7 +1331,7 @@ AhciDmaTransfer (
// and on next call the command will be re-issued due to IsStart being FALSE.
// This also makes the next condition decrement the RetryTimes.
//
- if (RecoveryStatus == EFI_SUCCESS) {
+ if (DoRetry && (RecoveryStatus == EFI_SUCCESS)) {
Task->IsStart = FALSE;
Status = EFI_NOT_READY;
}
@@ -1378,6 +1439,7 @@ AhciNonDataTransfer (
EFI_AHCI_COMMAND_LIST CmdList;
UINT32 Retry;
EFI_STATUS RecoveryStatus;
+ BOOLEAN DoRetry;
//
// Package read needed
@@ -1418,8 +1480,9 @@ AhciNonDataTransfer (
Status = AhciWaitUntilFisReceived (PciIo, Port, Timeout, SataFisD2H);
if (Status == EFI_DEVICE_ERROR) {
DEBUG ((DEBUG_ERROR, "Non data transfer failed at retry %d\n", Retry));
+ DoRetry = AhciShouldCmdBeRetried (PciIo, Port); // call this before error recovery
RecoveryStatus = AhciRecoverPortError (PciIo, Port);
- if (EFI_ERROR (RecoveryStatus)) {
+ if (!DoRetry || EFI_ERROR (RecoveryStatus)) {
break;
}
} else {
diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h
index d7434b408c..5bb31057ec 100644
--- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h
+++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.h
@@ -146,7 +146,8 @@ typedef union {
#define EFI_AHCI_PORT_TFD_BSY BIT7
#define EFI_AHCI_PORT_TFD_DRQ BIT3
#define EFI_AHCI_PORT_TFD_ERR BIT0
-#define EFI_AHCI_PORT_TFD_ERR_MASK 0x00FF00
+#define EFI_AHCI_PORT_TFD_ERR_MASK 0x00FF00 // ERROR field is specified by ATA/ATAPI Command Set specification
+#define EFI_AHCI_PORT_TFD_ERR_INT_CRC BIT15
#define EFI_AHCI_PORT_SIG 0x0024
#define EFI_AHCI_PORT_SSTS 0x0028
#define EFI_AHCI_PORT_SSTS_DET_MASK 0x000F