diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c index 7fc7a28126..72b1e5cc7e 100644 --- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c +++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c @@ -704,6 +704,8 @@ AhciPioTransfer ( UINT32 PortTfd; UINT32 PrdCount; BOOLEAN InfiniteWait; + BOOLEAN PioFisReceived; + BOOLEAN D2hFisReceived; if (Timeout == 0) { InfiniteWait = TRUE; @@ -780,15 +782,31 @@ AhciPioTransfer ( Status = EFI_TIMEOUT; Delay = DivU64x32 (Timeout, 1000) + 1; do { + PioFisReceived = FALSE; + D2hFisReceived = FALSE; Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET; - Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, NULL); if (!EFI_ERROR (Status)) { + PioFisReceived = TRUE; + } + // + // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered. + // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from device + // after the transaction is finished successfully. + // To get better device compatibilities, we further check if the PxTFD's ERR bit is set. + // By this way, we can know if there is a real error happened. + // + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; + Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL); + if (!EFI_ERROR (Status)) { + D2hFisReceived = TRUE; + } + + if (PioFisReceived || D2hFisReceived) { Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; PortTfd = AhciReadReg (PciIo, (UINT32) Offset); // // PxTFD will be updated if there is a D2H or SetupFIS received. - // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS. // if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { Status = EFI_DEVICE_ERROR; @@ -797,23 +815,20 @@ AhciPioTransfer ( PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc)); if (PrdCount == DataCount) { + Status = EFI_SUCCESS; break; } } - Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; - Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL); - if (!EFI_ERROR (Status)) { - Status = EFI_DEVICE_ERROR; - break; - } - // // Stall for 100 microseconds. // MicroSecondDelay(100); Delay--; + if (Delay == 0) { + Status = EFI_TIMEOUT; + } } while (InfiniteWait || (Delay > 0)); } else { //