--- /dev/null
+/** @file\r
+ The AhciPei driver is used to manage ATA hard disk device working under AHCI\r
+ mode at PEI phase.\r
+\r
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions\r
+ of the BSD License which accompanies this distribution. The\r
+ full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "AhciPei.h"\r
+\r
+#define ATA_CMD_TRUST_NON_DATA 0x5B\r
+#define ATA_CMD_TRUST_RECEIVE 0x5C\r
+#define ATA_CMD_TRUST_SEND 0x5E\r
+\r
+//\r
+// Look up table (IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL\r
+//\r
+EFI_ATA_PASS_THRU_CMD_PROTOCOL mAtaPassThruCmdProtocols[2] = {\r
+ EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN,\r
+ EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT\r
+};\r
+\r
+//\r
+// Look up table (Lba48Bit, IsIsWrite) for ATA_CMD\r
+//\r
+UINT8 mAtaCommands[2][2] = {\r
+ {\r
+ ATA_CMD_READ_SECTORS, // 28-bit LBA; PIO read\r
+ ATA_CMD_WRITE_SECTORS // 28-bit LBA; PIO write\r
+ },\r
+ {\r
+ ATA_CMD_READ_SECTORS_EXT, // 48-bit LBA; PIO read\r
+ ATA_CMD_WRITE_SECTORS_EXT // 48-bit LBA; PIO write\r
+ }\r
+};\r
+\r
+//\r
+// Look up table (IsTrustSend) for ATA_CMD\r
+//\r
+UINT8 mAtaTrustCommands[2] = {\r
+ ATA_CMD_TRUST_RECEIVE, // PIO read\r
+ ATA_CMD_TRUST_SEND // PIO write\r
+};\r
+\r
+//\r
+// Look up table (Lba48Bit) for maximum transfer block number\r
+//\r
+#define MAX_28BIT_TRANSFER_BLOCK_NUM 0x100\r
+#define MAX_48BIT_TRANSFER_BLOCK_NUM 0xFFFF\r
+\r
+UINT32 mMaxTransferBlockNumber[2] = {\r
+ MAX_28BIT_TRANSFER_BLOCK_NUM,\r
+ MAX_48BIT_TRANSFER_BLOCK_NUM\r
+};\r
+\r
+//\r
+// The maximum total sectors count in 28 bit addressing mode\r
+//\r
+#define MAX_28BIT_ADDRESSING_CAPACITY 0xfffffff\r
+\r
+\r
+/**\r
+ Read AHCI Operation register.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Offset The operation register offset.\r
+\r
+ @return The register content read.\r
+\r
+**/\r
+UINT32\r
+AhciReadReg (\r
+ IN UINTN AhciBar,\r
+ IN UINT32 Offset\r
+ )\r
+{\r
+ UINT32 Data;\r
+\r
+ Data = 0;\r
+ Data = MmioRead32 (AhciBar + Offset);\r
+\r
+ return Data;\r
+}\r
+\r
+/**\r
+ Write AHCI Operation register.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Offset The operation register offset.\r
+ @param[in] Data The Data used to write down.\r
+\r
+**/\r
+VOID\r
+AhciWriteReg (\r
+ IN UINTN AhciBar,\r
+ IN UINT32 Offset,\r
+ IN UINT32 Data\r
+ )\r
+{\r
+ MmioWrite32 (AhciBar + Offset, Data);\r
+}\r
+\r
+/**\r
+ Do AND operation with the value of AHCI Operation register.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Offset The operation register offset.\r
+ @param[in] AndData The data used to do AND operation.\r
+\r
+**/\r
+VOID\r
+AhciAndReg (\r
+ IN UINTN AhciBar,\r
+ IN UINT32 Offset,\r
+ IN UINT32 AndData\r
+ )\r
+{\r
+ UINT32 Data;\r
+\r
+ Data = AhciReadReg (AhciBar, Offset);\r
+ Data &= AndData;\r
+\r
+ AhciWriteReg (AhciBar, Offset, Data);\r
+}\r
+\r
+/**\r
+ Do OR operation with the Value of AHCI Operation register.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Offset The operation register offset.\r
+ @param[in] OrData The Data used to do OR operation.\r
+\r
+**/\r
+VOID\r
+AhciOrReg (\r
+ IN UINTN AhciBar,\r
+ IN UINT32 Offset,\r
+ IN UINT32 OrData\r
+ )\r
+{\r
+ UINT32 Data;\r
+\r
+ Data = AhciReadReg (AhciBar, Offset);\r
+ Data |= OrData;\r
+\r
+ AhciWriteReg (AhciBar, Offset, Data);\r
+}\r
+\r
+/**\r
+ Wait for memory set to the test Value.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Offset The memory offset to test.\r
+ @param[in] MaskValue The mask Value of memory.\r
+ @param[in] TestValue The test Value of memory.\r
+ @param[in] Timeout The timeout, in 100ns units, for wait memory set.\r
+\r
+ @retval EFI_DEVICE_ERROR The memory is not set.\r
+ @retval EFI_TIMEOUT The memory setting is time out.\r
+ @retval EFI_SUCCESS The memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciWaitMmioSet (\r
+ IN UINTN AhciBar,\r
+ IN UINT32 Offset,\r
+ IN UINT32 MaskValue,\r
+ IN UINT32 TestValue,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Value;\r
+ UINT32 Delay;\r
+\r
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+\r
+ do {\r
+ Value = AhciReadReg (AhciBar, Offset) & MaskValue;\r
+\r
+ if (Value == TestValue) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Stall for 100 microseconds.\r
+ //\r
+ MicroSecondDelay (100);\r
+\r
+ Delay--;\r
+\r
+ } while (Delay > 0);\r
+\r
+ return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+ Check the memory status to the test value.\r
+\r
+ @param[in] Address The memory address to test.\r
+ @param[in] MaskValue The mask value of memory.\r
+ @param[in] TestValue The test value of memory.\r
+\r
+ @retval EFI_NOT_READY The memory is not set.\r
+ @retval EFI_SUCCESS The memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciCheckMemSet (\r
+ IN UINTN Address,\r
+ IN UINT32 MaskValue,\r
+ IN UINT32 TestValue\r
+ )\r
+{\r
+ UINT32 Value;\r
+\r
+ Value = *(volatile UINT32 *) Address;\r
+ Value &= MaskValue;\r
+\r
+ if (Value == TestValue) {\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ return EFI_NOT_READY;\r
+ }\r
+}\r
+\r
+/**\r
+ Wait for the value of the specified system memory set to the test value.\r
+\r
+ @param[in] Address The system memory address to test.\r
+ @param[in] MaskValue The mask value of memory.\r
+ @param[in] TestValue The test value of memory.\r
+ @param[in] Timeout The timeout, in 100ns units, for wait memory set.\r
+\r
+ @retval EFI_TIMEOUT The system memory setting is time out.\r
+ @retval EFI_SUCCESS The system memory is correct set.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciWaitMemSet (\r
+ IN EFI_PHYSICAL_ADDRESS Address,\r
+ IN UINT32 MaskValue,\r
+ IN UINT32 TestValue,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Value;\r
+ UINT64 Delay;\r
+ BOOLEAN InfiniteWait;\r
+\r
+ if (Timeout == 0) {\r
+ InfiniteWait = TRUE;\r
+ } else {\r
+ InfiniteWait = FALSE;\r
+ }\r
+\r
+ Delay = DivU64x32 (Timeout, 1000) + 1;\r
+\r
+ do {\r
+ //\r
+ // Access sytem memory to see if the value is the tested one.\r
+ //\r
+ // The system memory pointed by Address will be updated by the\r
+ // SATA Host Controller, "volatile" is introduced to prevent\r
+ // compiler from optimizing the access to the memory address\r
+ // to only read once.\r
+ //\r
+ Value = *(volatile UINT32 *) (UINTN) Address;\r
+ Value &= MaskValue;\r
+\r
+ if (Value == TestValue) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Stall for 100 microseconds.\r
+ //\r
+ MicroSecondDelay (100);\r
+\r
+ Delay--;\r
+\r
+ } while (InfiniteWait || (Delay > 0));\r
+\r
+ return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+\r
+ Clear the port interrupt and error status. It will also clear HBA interrupt\r
+ status.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Port The number of port.\r
+\r
+**/\r
+VOID\r
+AhciClearPortStatus (\r
+ IN UINTN AhciBar,\r
+ IN UINT8 Port\r
+ )\r
+{\r
+ UINT32 Offset;\r
+\r
+ //\r
+ // Clear any error status\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
+ AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
+\r
+ //\r
+ // Clear any port interrupt status\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IS;\r
+ AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
+\r
+ //\r
+ // Clear any HBA interrupt status\r
+ //\r
+ AhciWriteReg (AhciBar, AHCI_IS_OFFSET, AhciReadReg (AhciBar, AHCI_IS_OFFSET));\r
+}\r
+\r
+/**\r
+ Enable the FIS running for giving port.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Port The number of port.\r
+ @param[in] Timeout The timeout, in 100ns units, to enabling FIS.\r
+\r
+ @retval EFI_DEVICE_ERROR The FIS enable setting fails.\r
+ @retval EFI_TIMEOUT The FIS enable setting is time out.\r
+ @retval EFI_SUCCESS The FIS enable successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciEnableFisReceive (\r
+ IN UINTN AhciBar,\r
+ IN UINT8 Port,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Offset;\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Disable the FIS running for giving port.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Port The number of port.\r
+ @param[in] Timeout The timeout value of disabling FIS, uses 100ns as a unit.\r
+\r
+ @retval EFI_DEVICE_ERROR The FIS disable setting fails.\r
+ @retval EFI_TIMEOUT The FIS disable setting is time out.\r
+ @retval EFI_UNSUPPORTED The port is in running state.\r
+ @retval EFI_SUCCESS The FIS disable successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciDisableFisReceive (\r
+ IN UINTN AhciBar,\r
+ IN UINT8 Port,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Offset;\r
+ UINT32 Data;\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ Data = AhciReadReg (AhciBar, Offset);\r
+\r
+ //\r
+ // Before disabling Fis receive, the DMA engine of the port should NOT be in\r
+ // running status.\r
+ //\r
+ if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) != 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Check if the Fis receive DMA engine for the port is running.\r
+ //\r
+ if ((Data & AHCI_PORT_CMD_FR) != AHCI_PORT_CMD_FR) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_FRE));\r
+\r
+ return AhciWaitMmioSet (\r
+ AhciBar,\r
+ Offset,\r
+ AHCI_PORT_CMD_FR,\r
+ 0,\r
+ Timeout\r
+ );\r
+}\r
+\r
+/**\r
+ Build the command list, command table and prepare the fis receiver.\r
+\r
+ @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+ @param[in] Port The number of port.\r
+ @param[in] PortMultiplier The number of port multiplier.\r
+ @param[in] FisIndex The offset index of the FIS base address.\r
+ @param[in] CommandFis The control fis will be used for the transfer.\r
+ @param[in] CommandList The command list will be used for the transfer.\r
+ @param[in] CommandSlotNumber The command slot will be used for the transfer.\r
+ @param[in,out] DataPhysicalAddr The pointer to the data buffer pci bus master\r
+ address.\r
+ @param[in] DataLength The data count to be transferred.\r
+\r
+**/\r
+VOID\r
+AhciBuildCommand (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT8 Port,\r
+ IN UINT8 PortMultiplier,\r
+ IN UINT8 FisIndex,\r
+ IN EFI_AHCI_COMMAND_FIS *CommandFis,\r
+ IN EFI_AHCI_COMMAND_LIST *CommandList,\r
+ IN UINT8 CommandSlotNumber,\r
+ IN OUT VOID *DataPhysicalAddr,\r
+ IN UINT32 DataLength\r
+ )\r
+{\r
+ EFI_AHCI_REGISTERS *AhciRegisters;\r
+ UINTN AhciBar;\r
+ UINT64 BaseAddr;\r
+ UINT32 PrdtNumber;\r
+ UINT32 PrdtIndex;\r
+ UINTN RemainedData;\r
+ UINTN MemAddr;\r
+ DATA_64 Data64;\r
+ UINT32 Offset;\r
+\r
+ AhciRegisters = &Private->AhciRegisters;\r
+ AhciBar = Private->MmioBase;\r
+\r
+ //\r
+ // Filling the PRDT\r
+ //\r
+ PrdtNumber = (UINT32)DivU64x32 (\r
+ (UINT64)DataLength + AHCI_MAX_DATA_PER_PRDT - 1,\r
+ AHCI_MAX_DATA_PER_PRDT\r
+ );\r
+\r
+ //\r
+ // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.\r
+ // It also limits that the maximum amount of the PRDT entry in the command table\r
+ // is 65535.\r
+ // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
+ // PRDT entries.\r
+ //\r
+ ASSERT (PrdtNumber <= AHCI_MAX_PRDT_NUMBER);\r
+ if (PrdtNumber > AHCI_MAX_PRDT_NUMBER) {\r
+ return;\r
+ }\r
+\r
+ Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+\r
+ BaseAddr = Data64.Uint64;\r
+\r
+ ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
+\r
+ ZeroMem (AhciRegisters->AhciCmdTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
+\r
+ CommandFis->AhciCFisPmNum = PortMultiplier;\r
+\r
+ CopyMem (&AhciRegisters->AhciCmdTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_DLAE | AHCI_PORT_CMD_ATAPI));\r
+\r
+ RemainedData = (UINTN) DataLength;\r
+ MemAddr = (UINTN) DataPhysicalAddr;\r
+ CommandList->AhciCmdPrdtl = PrdtNumber;\r
+\r
+ for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
+ if (RemainedData < AHCI_MAX_DATA_PER_PRDT) {\r
+ AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;\r
+ } else {\r
+ AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = AHCI_MAX_DATA_PER_PRDT - 1;\r
+ }\r
+\r
+ Data64.Uint64 = (UINT64)MemAddr;\r
+ AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDba = Data64.Uint32.Lower32;\r
+ AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;\r
+ RemainedData -= AHCI_MAX_DATA_PER_PRDT;\r
+ MemAddr += AHCI_MAX_DATA_PER_PRDT;\r
+ }\r
+\r
+ //\r
+ // Set the last PRDT to Interrupt On Complete\r
+ //\r
+ if (PrdtNumber > 0) {\r
+ AhciRegisters->AhciCmdTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;\r
+ }\r
+\r
+ CopyMem (\r
+ (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),\r
+ CommandList,\r
+ sizeof (EFI_AHCI_COMMAND_LIST)\r
+ );\r
+\r
+ Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCmdTable;\r
+ AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;\r
+ AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;\r
+ AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;\r
+}\r
+\r
+/**\r
+ Buid a command FIS.\r
+\r
+ @param[in,out] CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data\r
+ structure.\r
+ @param[in] AtaCommandBlock A pointer to the EFI_ATA_COMMAND_BLOCK data\r
+ structure.\r
+\r
+**/\r
+VOID\r
+AhciBuildCommandFis (\r
+ IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,\r
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock\r
+ )\r
+{\r
+ ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
+\r
+ CmdFis->AhciCFisType = AHCI_FIS_REGISTER_H2D;\r
+ //\r
+ // Indicator it's a command\r
+ //\r
+ CmdFis->AhciCFisCmdInd = 0x1;\r
+ CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;\r
+\r
+ CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;\r
+ CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;\r
+\r
+ CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;\r
+ CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;\r
+\r
+ CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;\r
+ CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;\r
+\r
+ CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;\r
+ CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;\r
+\r
+ CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;\r
+ CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;\r
+\r
+ CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
+}\r
+\r
+/**\r
+ Stop command running for giving port\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Port The number of port.\r
+ @param[in] Timeout The timeout value, in 100ns units, to stop.\r
+\r
+ @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
+ @retval EFI_TIMEOUT The operation is time out.\r
+ @retval EFI_SUCCESS The command stop successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciStopCommand (\r
+ IN UINTN AhciBar,\r
+ IN UINT8 Port,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Offset;\r
+ UINT32 Data;\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ Data = AhciReadReg (AhciBar, Offset);\r
+\r
+ if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if ((Data & AHCI_PORT_CMD_ST) != 0) {\r
+ AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_ST));\r
+ }\r
+\r
+ return AhciWaitMmioSet (\r
+ AhciBar,\r
+ Offset,\r
+ AHCI_PORT_CMD_CR,\r
+ 0,\r
+ Timeout\r
+ );\r
+}\r
+\r
+/**\r
+ Start command for give slot on specific port.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Port The number of port.\r
+ @param[in] CommandSlot The number of Command Slot.\r
+ @param[in] Timeout The timeout value, in 100ns units, to start.\r
+\r
+ @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
+ @retval EFI_TIMEOUT The operation is time out.\r
+ @retval EFI_SUCCESS The command start successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciStartCommand (\r
+ IN UINTN AhciBar,\r
+ IN UINT8 Port,\r
+ IN UINT8 CommandSlot,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 CmdSlotBit;\r
+ EFI_STATUS Status;\r
+ UINT32 PortStatus;\r
+ UINT32 StartCmd;\r
+ UINT32 PortTfd;\r
+ UINT32 Offset;\r
+ UINT32 Capability;\r
+\r
+ //\r
+ // Collect AHCI controller information\r
+ //\r
+ Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+ CmdSlotBit = (UINT32) (1 << CommandSlot);\r
+\r
+ AhciClearPortStatus (\r
+ AhciBar,\r
+ Port\r
+ );\r
+\r
+ Status = AhciEnableFisReceive (\r
+ AhciBar,\r
+ Port,\r
+ Timeout\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ PortStatus = AhciReadReg (AhciBar, Offset);\r
+\r
+ StartCmd = 0;\r
+ if ((PortStatus & AHCI_PORT_CMD_ALPE) != 0) {\r
+ StartCmd = AhciReadReg (AhciBar, Offset);\r
+ StartCmd &= ~AHCI_PORT_CMD_ICC_MASK;\r
+ StartCmd |= AHCI_PORT_CMD_ACTIVE;\r
+ }\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+ PortTfd = AhciReadReg (AhciBar, Offset);\r
+\r
+ if ((PortTfd & (AHCI_PORT_TFD_BSY | AHCI_PORT_TFD_DRQ)) != 0) {\r
+ if ((Capability & BIT24) != 0) {\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_CLO);\r
+\r
+ AhciWaitMmioSet (\r
+ AhciBar,\r
+ Offset,\r
+ AHCI_PORT_CMD_CLO,\r
+ 0,\r
+ Timeout\r
+ );\r
+ }\r
+ }\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_ST | StartCmd);\r
+\r
+ //\r
+ // Setting the command\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CI;\r
+ AhciAndReg (AhciBar, Offset, 0);\r
+ AhciOrReg (AhciBar, Offset, CmdSlotBit);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Start a PIO Data transfer on specific port.\r
+\r
+ @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+ @param[in] Port The number of port.\r
+ @param[in] PortMultiplier The number of port multiplier.\r
+ @param[in] FisIndex The offset index of the FIS base address.\r
+ @param[in] Read The transfer direction.\r
+ @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
+ @param[in,out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
+ @param[in,out] MemoryAddr The pointer to the data buffer.\r
+ @param[in] DataCount The data count to be transferred.\r
+ @param[in] Timeout The timeout value of PIO data transfer, uses\r
+ 100ns as a unit.\r
+\r
+ @retval EFI_DEVICE_ERROR The PIO data transfer abort with error occurs.\r
+ @retval EFI_TIMEOUT The operation is time out.\r
+ @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
+ @retval EFI_SUCCESS The PIO data transfer executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciPioTransfer (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT8 Port,\r
+ IN UINT8 PortMultiplier,\r
+ IN UINT8 FisIndex,\r
+ IN BOOLEAN Read,\r
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
+ IN OUT VOID *MemoryAddr,\r
+ IN UINT32 DataCount,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EDKII_IOMMU_OPERATION MapOp;\r
+ UINTN MapLength;\r
+ EFI_PHYSICAL_ADDRESS PhyAddr;\r
+ VOID *MapData;\r
+ EFI_AHCI_REGISTERS *AhciRegisters;\r
+ UINTN AhciBar;\r
+ BOOLEAN InfiniteWait;\r
+ UINT32 Offset;\r
+ UINT32 OldRfisLo;\r
+ UINT32 OldRfisHi;\r
+ UINT32 OldCmdListLo;\r
+ UINT32 OldCmdListHi;\r
+ DATA_64 Data64;\r
+ UINT32 FisBaseAddr;\r
+ UINT32 Delay;\r
+ EFI_AHCI_COMMAND_FIS CFis;\r
+ EFI_AHCI_COMMAND_LIST CmdList;\r
+ UINT32 PortTfd;\r
+ UINT32 PrdCount;\r
+ BOOLEAN PioFisReceived;\r
+ BOOLEAN D2hFisReceived;\r
+\r
+ //\r
+ // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
+ // PRDT entries.\r
+ //\r
+ if (DataCount / (UINT32)AHCI_MAX_PRDT_NUMBER > AHCI_MAX_DATA_PER_PRDT) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Driver only support a maximum of 0x%x PRDT entries, "\r
+ "current number of data byte 0x%x is too large, maximum allowed is 0x%x.\n",\r
+ __FUNCTION__, AHCI_MAX_PRDT_NUMBER, DataCount,\r
+ AHCI_MAX_PRDT_NUMBER * AHCI_MAX_DATA_PER_PRDT\r
+ ));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ MapOp = Read ? EdkiiIoMmuOperationBusMasterWrite :\r
+ EdkiiIoMmuOperationBusMasterRead;\r
+ MapLength = DataCount;\r
+ Status = IoMmuMap (\r
+ MapOp,\r
+ MemoryAddr,\r
+ &MapLength,\r
+ &PhyAddr,\r
+ &MapData\r
+ );\r
+ if (EFI_ERROR (Status) || (MapLength != DataCount)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ AhciRegisters = &Private->AhciRegisters;\r
+ AhciBar = Private->MmioBase;\r
+ InfiniteWait = (Timeout == 0) ? TRUE : FALSE;\r
+\r
+ //\r
+ // Fill FIS base address register\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+ OldRfisLo = AhciReadReg (AhciBar, Offset);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+ OldRfisHi = AhciReadReg (AhciBar, Offset);\r
+ Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+ //\r
+ // Single task envrionment, we only use one command table for all port\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+ OldCmdListLo = AhciReadReg (AhciBar, Offset);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+ OldCmdListHi = AhciReadReg (AhciBar, Offset);\r
+ Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+ //\r
+ // Package read needed\r
+ //\r
+ AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
+\r
+ ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
+\r
+ CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
+ CmdList.AhciCmdW = Read ? 0 : 1;\r
+\r
+ AhciBuildCommand (\r
+ Private,\r
+ Port,\r
+ PortMultiplier,\r
+ FisIndex,\r
+ &CFis,\r
+ &CmdList,\r
+ 0,\r
+ (VOID *)(UINTN)PhyAddr,\r
+ DataCount\r
+ );\r
+\r
+ Status = AhciStartCommand (\r
+ AhciBar,\r
+ Port,\r
+ 0,\r
+ Timeout\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Checking the status and wait the driver sending Data\r
+ //\r
+ FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+ if (Read) {\r
+ //\r
+ // Wait device sends the PIO setup fis before data transfer\r
+ //\r
+ Status = EFI_TIMEOUT;\r
+ Delay = (UINT32) DivU64x32 (Timeout, 1000) + 1;\r
+ do {\r
+ PioFisReceived = FALSE;\r
+ D2hFisReceived = FALSE;\r
+ Offset = FisBaseAddr + AHCI_PIO_FIS_OFFSET;\r
+ Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_PIO_SETUP);\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO, "%a: PioFisReceived.\n", __FUNCTION__));\r
+ PioFisReceived = TRUE;\r
+ }\r
+ //\r
+ // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
+ // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from\r
+ // device after the transaction is finished successfully.\r
+ // To get better device compatibilities, we further check if the PxTFD's\r
+ // ERR bit is set. By this way, we can know if there is a real error happened.\r
+ //\r
+ Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
+ Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_REGISTER_D2H);\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO, "%a: D2hFisReceived.\n", __FUNCTION__));\r
+ D2hFisReceived = TRUE;\r
+ }\r
+\r
+ if (PioFisReceived || D2hFisReceived) {\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+ PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
+ //\r
+ // PxTFD will be updated if there is a D2H or SetupFIS received.\r
+ //\r
+ if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ break;\r
+ }\r
+\r
+ PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
+ if (PrdCount == DataCount) {\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Stall for 100 microseconds.\r
+ //\r
+ MicroSecondDelay(100);\r
+\r
+ Delay--;\r
+ if (Delay == 0) {\r
+ Status = EFI_TIMEOUT;\r
+ }\r
+ } while (InfiniteWait || (Delay > 0));\r
+ } else {\r
+ //\r
+ // Wait for D2H Fis is received\r
+ //\r
+ Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
+ Status = AhciWaitMemSet (\r
+ Offset,\r
+ AHCI_FIS_TYPE_MASK,\r
+ AHCI_FIS_REGISTER_D2H,\r
+ Timeout\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: AhciWaitMemSet (%r)\n", __FUNCTION__, Status));\r
+ goto Exit;\r
+ }\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+ PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
+ if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+Exit:\r
+ AhciStopCommand (\r
+ AhciBar,\r
+ Port,\r
+ Timeout\r
+ );\r
+\r
+ AhciDisableFisReceive (\r
+ AhciBar,\r
+ Port,\r
+ Timeout\r
+ );\r
+\r
+ if (MapData != NULL) {\r
+ IoMmuUnmap (MapData);\r
+ }\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+ AhciWriteReg (AhciBar, Offset, OldRfisLo);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+ AhciWriteReg (AhciBar, Offset, OldRfisHi);\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+ AhciWriteReg (AhciBar, Offset, OldCmdListLo);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+ AhciWriteReg (AhciBar, Offset, OldCmdListHi);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Start a non data transfer on specific port.\r
+\r
+ @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+ @param[in] Port The number of port.\r
+ @param[in] PortMultiplier The number of port multiplier.\r
+ @param[in] FisIndex The offset index of the FIS base address.\r
+ @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
+ @param[in,out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
+ @param[in] Timeout The timeout value of non data transfer, uses\r
+ 100ns as a unit.\r
+\r
+ @retval EFI_DEVICE_ERROR The non data transfer abort with error occurs.\r
+ @retval EFI_TIMEOUT The operation is time out.\r
+ @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
+ @retval EFI_SUCCESS The non data transfer executes successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciNonDataTransfer (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT8 Port,\r
+ IN UINT8 PortMultiplier,\r
+ IN UINT8 FisIndex,\r
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN AhciBar;\r
+ EFI_AHCI_REGISTERS *AhciRegisters;\r
+ UINTN FisBaseAddr;\r
+ UINTN Offset;\r
+ UINT32 PortTfd;\r
+ EFI_AHCI_COMMAND_FIS CFis;\r
+ EFI_AHCI_COMMAND_LIST CmdList;\r
+\r
+ AhciBar = Private->MmioBase;\r
+ AhciRegisters = &Private->AhciRegisters;\r
+\r
+ //\r
+ // Package read needed\r
+ //\r
+ AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
+\r
+ ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
+\r
+ CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
+\r
+ AhciBuildCommand (\r
+ Private,\r
+ Port,\r
+ PortMultiplier,\r
+ FisIndex,\r
+ &CFis,\r
+ &CmdList,\r
+ 0,\r
+ NULL,\r
+ 0\r
+ );\r
+\r
+ Status = AhciStartCommand (\r
+ AhciBar,\r
+ Port,\r
+ 0,\r
+ Timeout\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ //\r
+ // Wait device sends the Response Fis\r
+ //\r
+ FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
+ Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
+ Status = AhciWaitMemSet (\r
+ Offset,\r
+ AHCI_FIS_TYPE_MASK,\r
+ AHCI_FIS_REGISTER_D2H,\r
+ Timeout\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Exit;\r
+ }\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+ PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
+ if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+Exit:\r
+ AhciStopCommand (\r
+ AhciBar,\r
+ Port,\r
+ Timeout\r
+ );\r
+\r
+ AhciDisableFisReceive (\r
+ AhciBar,\r
+ Port,\r
+ Timeout\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Do AHCI HBA reset.\r
+\r
+ @param[in] AhciBar AHCI bar address.\r
+ @param[in] Timeout The timeout, in 100ns units, to reset.\r
+\r
+ @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.\r
+ @retval EFI_TIMEOUT The reset operation is time out.\r
+ @retval EFI_SUCCESS AHCI controller is reset successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciReset (\r
+ IN UINTN AhciBar,\r
+ IN UINT64 Timeout\r
+ )\r
+{\r
+ UINT32 Delay;\r
+ UINT32 Value;\r
+ UINT32 Capability;\r
+\r
+ //\r
+ // Collect AHCI controller information\r
+ //\r
+ Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+ //\r
+ // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
+ //\r
+ if ((Capability & AHCI_CAP_SAM) == 0) {\r
+ AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
+ }\r
+\r
+ AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_RESET);\r
+\r
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
+\r
+ do {\r
+ Value = AhciReadReg(AhciBar, AHCI_GHC_OFFSET);\r
+ if ((Value & AHCI_GHC_RESET) == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Stall for 100 microseconds.\r
+ //\r
+ MicroSecondDelay(100);\r
+\r
+ Delay--;\r
+ } while (Delay > 0);\r
+\r
+ return EFI_TIMEOUT;\r
+}\r
+\r
+/**\r
+ Send Identify Drive command to a specific device.\r
+\r
+ @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+ @param[in] Port The number of port.\r
+ @param[in] PortMultiplier The port multiplier port number.\r
+ @param[in] FisIndex The offset index of the FIS base address.\r
+ @param[in] Buffer The data buffer to store IDENTIFY PACKET data.\r
+\r
+ @retval EFI_SUCCESS The cmd executes successfully.\r
+ @retval EFI_INVALID_PARAMETER Buffer is NULL.\r
+ @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
+ @retval EFI_TIMEOUT The operation is time out.\r
+ @retval EFI_UNSUPPORTED The device is not ready for executing.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciIdentify (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT8 Port,\r
+ IN UINT8 PortMultiplier,\r
+ IN UINT8 FisIndex,\r
+ IN ATA_IDENTIFY_DATA *Buffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ATA_COMMAND_BLOCK Acb;\r
+ EFI_ATA_STATUS_BLOCK Asb;\r
+\r
+ if (Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+ ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));\r
+\r
+ Acb.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;\r
+ Acb.AtaSectorCount = 1;\r
+\r
+ Status = AhciPioTransfer (\r
+ Private,\r
+ Port,\r
+ PortMultiplier,\r
+ FisIndex,\r
+ TRUE,\r
+ &Acb,\r
+ &Asb,\r
+ Buffer,\r
+ sizeof (ATA_IDENTIFY_DATA),\r
+ ATA_TIMEOUT\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Collect the number of bits set within a port bitmap.\r
+\r
+ @param[in] PortBitMap A 32-bit wide bit map of ATA AHCI ports.\r
+\r
+ @retval The number of bits set in the bitmap.\r
+\r
+**/\r
+UINT8\r
+AhciGetNumberOfPortsFromMap (\r
+ IN UINT32 PortBitMap\r
+ )\r
+{\r
+ UINT8 NumberOfPorts;\r
+\r
+ NumberOfPorts = 0;\r
+\r
+ while (PortBitMap != 0) {\r
+ if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
+ NumberOfPorts++;\r
+ }\r
+ PortBitMap = PortBitMap >> 1;\r
+ }\r
+\r
+ return NumberOfPorts;\r
+}\r
+\r
+/**\r
+ Get the specified port number from a port bitmap.\r
+\r
+ @param[in] PortBitMap A 32-bit wide bit map of ATA AHCI ports.\r
+ @param[in] PortIndex The specified port index.\r
+ @param[out] Port The port number of the port specified by PortIndex.\r
+\r
+ @retval EFI_SUCCESS The specified port is found and its port number is\r
+ in Port.\r
+ @retval EFI_NOT_FOUND Cannot find the specified port within the port bitmap.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciGetPortFromMap (\r
+ IN UINT32 PortBitMap,\r
+ IN UINT8 PortIndex,\r
+ OUT UINT8 *Port\r
+ )\r
+{\r
+ if (PortIndex == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ *Port = 0;\r
+\r
+ while (PortBitMap != 0) {\r
+ if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
+ PortIndex--;\r
+\r
+ //\r
+ // Found the port specified by PortIndex.\r
+ //\r
+ if (PortIndex == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ PortBitMap = PortBitMap >> 1;\r
+ *Port = *Port + 1;\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Allocate transfer-related data struct which is used at AHCI mode.\r
+\r
+ @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
+\r
+ @retval EFI_SUCCESS Data structures are allocated successfully.\r
+ @retval Others Data structures are not allocated successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciCreateTransferDescriptor (\r
+ IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN AhciBar;\r
+ EFI_AHCI_REGISTERS *AhciRegisters;\r
+ EFI_PHYSICAL_ADDRESS DeviceAddress;\r
+ VOID *Base;\r
+ VOID *Mapping;\r
+ UINT32 Capability;\r
+ UINT32 PortImplementBitMap;\r
+ UINT8 MaxPortNumber;\r
+ UINT8 MaxCommandSlotNumber;\r
+ UINTN MaxRFisSize;\r
+ UINTN MaxCmdListSize;\r
+ UINTN MaxCmdTableSize;\r
+\r
+ AhciBar = Private->MmioBase;\r
+ AhciRegisters = &Private->AhciRegisters;\r
+\r
+ //\r
+ // Collect AHCI controller information\r
+ //\r
+ Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+ //\r
+ // Get the number of command slots per port supported by this HBA.\r
+ //\r
+ MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
+ ASSERT (MaxCommandSlotNumber > 0);\r
+ if (MaxCommandSlotNumber == 0) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Get the highest bit of implemented ports which decides how many bytes are\r
+ // allocated for recived FIS.\r
+ //\r
+ PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
+ MaxPortNumber = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
+ if (MaxPortNumber == 0) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // Get the number of ports that actually needed to be initialized.\r
+ //\r
+ MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
+\r
+ //\r
+ // Allocate memory for received FIS.\r
+ //\r
+ MaxRFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
+ Status = IoMmuAllocateBuffer (\r
+ EFI_SIZE_TO_PAGES (MaxRFisSize),\r
+ &Base,\r
+ &DeviceAddress,\r
+ &Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
+ AhciRegisters->AhciRFis = Base;\r
+ AhciRegisters->AhciRFisMap = Mapping;\r
+ AhciRegisters->MaxRFisSize = MaxRFisSize;\r
+ ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxRFisSize));\r
+\r
+ //\r
+ // Allocate memory for command list.\r
+ // Note that the implemenation is a single task model which only use a command\r
+ // list for each port.\r
+ //\r
+ MaxCmdListSize = 1 * sizeof (EFI_AHCI_COMMAND_LIST);\r
+ Status = IoMmuAllocateBuffer (\r
+ EFI_SIZE_TO_PAGES (MaxCmdListSize),\r
+ &Base,\r
+ &DeviceAddress,\r
+ &Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
+ ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
+ AhciRegisters->AhciCmdList = Base;\r
+ AhciRegisters->AhciCmdListMap = Mapping;\r
+ AhciRegisters->MaxCmdListSize = MaxCmdListSize;\r
+ ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdListSize));\r
+\r
+ //\r
+ // Allocate memory for command table\r
+ // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.\r
+ //\r
+ MaxCmdTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);\r
+ Status = IoMmuAllocateBuffer (\r
+ EFI_SIZE_TO_PAGES (MaxCmdTableSize),\r
+ &Base,\r
+ &DeviceAddress,\r
+ &Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
+ ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
+ AhciRegisters->AhciCmdTable = Base;\r
+ AhciRegisters->AhciCmdTableMap = Mapping;\r
+ AhciRegisters->MaxCmdTableSize = MaxCmdTableSize;\r
+ ZeroMem (AhciRegisters->AhciCmdTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdTableSize));\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ErrorExit:\r
+ if (AhciRegisters->AhciRFisMap != NULL) {\r
+ IoMmuFreeBuffer (\r
+ EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),\r
+ AhciRegisters->AhciRFis,\r
+ AhciRegisters->AhciRFisMap\r
+ );\r
+ AhciRegisters->AhciRFis = NULL;\r
+ }\r
+\r
+ if (AhciRegisters->AhciCmdListMap != NULL) {\r
+ IoMmuFreeBuffer (\r
+ EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),\r
+ AhciRegisters->AhciCmdList,\r
+ AhciRegisters->AhciCmdListMap\r
+ );\r
+ AhciRegisters->AhciCmdList = NULL;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Gets ATA device Capacity according to ATA 6.\r
+\r
+ This function returns the capacity of the ATA device if it follows\r
+ ATA 6 to support 48 bit addressing.\r
+\r
+ @param[in] IdentifyData A pointer to ATA_IDENTIFY_DATA structure.\r
+\r
+ @return The capacity of the ATA device or 0 if the device does not support\r
+ 48-bit addressing defined in ATA 6.\r
+\r
+**/\r
+EFI_LBA\r
+GetAtapi6Capacity (\r
+ IN ATA_IDENTIFY_DATA *IdentifyData\r
+ )\r
+{\r
+ EFI_LBA Capacity;\r
+ EFI_LBA TmpLba;\r
+ UINTN Index;\r
+\r
+ if ((IdentifyData->command_set_supported_83 & BIT10) == 0) {\r
+ //\r
+ // The device doesn't support 48 bit addressing\r
+ //\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // 48 bit address feature set is supported, get maximum capacity\r
+ //\r
+ Capacity = 0;\r
+ for (Index = 0; Index < 4; Index++) {\r
+ //\r
+ // Lower byte goes first: word[100] is the lowest word, word[103] is highest\r
+ //\r
+ TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index];\r
+ Capacity |= LShiftU64 (TmpLba, 16 * Index);\r
+ }\r
+\r
+ return Capacity;\r
+}\r
+\r
+/**\r
+ Identifies ATA device via the Identify data.\r
+\r
+ This function identifies the ATA device and initializes the media information.\r
+\r
+ @attention This is boundary function that may receive untrusted input.\r
+ @attention The input is from peripheral hardware device.\r
+\r
+ The Identify Drive command response data from an ATA device is the peripheral\r
+ hardware input, so this routine will do basic validation for the Identify Drive\r
+ command response data.\r
+\r
+ @param[in,out] DeviceData A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
+\r
+ @retval EFI_SUCCESS The device is successfully identified and media\r
+ information is correctly initialized.\r
+ @retval EFI_UNSUPPORTED The device is not a valid ATA device (hard disk).\r
+\r
+**/\r
+EFI_STATUS\r
+IdentifyAtaDevice (\r
+ IN OUT PEI_AHCI_ATA_DEVICE_DATA *DeviceData\r
+ )\r
+{\r
+ ATA_IDENTIFY_DATA *IdentifyData;\r
+ EFI_PEI_BLOCK_IO2_MEDIA *Media;\r
+ EFI_LBA Capacity;\r
+ UINT32 MaxSectorCount;\r
+ UINT16 PhyLogicSectorSupport;\r
+\r
+ IdentifyData = DeviceData->IdentifyData;\r
+ Media = &DeviceData->Media;\r
+\r
+ if ((IdentifyData->config & BIT15) != 0) {\r
+ DEBUG ((\r
+ DEBUG_ERROR, "%a: Not a hard disk device on Port 0x%x PortMultiplierPort 0x%x\n",\r
+ __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
+ ));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO, "%a: Identify Device: Port 0x%x PortMultiplierPort 0x%x\n",\r
+ __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
+ ));\r
+\r
+ //\r
+ // Skip checking whether the WORD 88 (supported UltraDMA by drive), since the\r
+ // driver only support PIO data transfer for now.\r
+ //\r
+\r
+ //\r
+ // Get the capacity information of the device.\r
+ //\r
+ Capacity = GetAtapi6Capacity (IdentifyData);\r
+ if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {\r
+ //\r
+ // Capacity exceeds 120GB. 48-bit addressing is really needed\r
+ //\r
+ DeviceData->Lba48Bit = TRUE;\r
+ } else {\r
+ //\r
+ // This is a hard disk <= 120GB capacity, treat it as normal hard disk\r
+ //\r
+ Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) |\r
+ IdentifyData->user_addressable_sectors_lo;\r
+ DeviceData->Lba48Bit = FALSE;\r
+ }\r
+\r
+ if (Capacity == 0) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Invalid Capacity (0) for ATA device.\n", __FUNCTION__));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ Media->LastBlock = (EFI_PEI_LBA) (Capacity - 1);\r
+\r
+ Media->BlockSize = 0x200;\r
+ //\r
+ // Check whether Long Physical Sector Feature is supported\r
+ //\r
+ PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support;\r
+ DEBUG ((\r
+ DEBUG_INFO, "%a: PhyLogicSectorSupport = 0x%x\n",\r
+ __FUNCTION__, PhyLogicSectorSupport\r
+ ));\r
+ if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) {\r
+ //\r
+ // Check logical block size\r
+ //\r
+ if ((PhyLogicSectorSupport & BIT12) != 0) {\r
+ Media->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |\r
+ IdentifyData->logic_sector_size_lo) * sizeof (UINT16));\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check BlockSize validity\r
+ //\r
+ MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
+ if ((Media->BlockSize == 0) || (Media->BlockSize > MAX_UINT32 / MaxSectorCount)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Invalid BlockSize (0x%x).\n", __FUNCTION__, Media->BlockSize));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO, "%a: BlockSize = 0x%x, LastBlock = 0x%lx\n",\r
+ __FUNCTION__, Media->BlockSize, Media->LastBlock\r
+ ));\r
+\r
+ if ((IdentifyData->trusted_computing_support & BIT0) != 0) {\r
+ DEBUG ((DEBUG_INFO, "%a: Found Trust Computing feature support.\n", __FUNCTION__));\r
+ DeviceData->TrustComputing = TRUE;\r
+ }\r
+\r
+ Media->InterfaceType = MSG_SATA_DP;\r
+ Media->RemovableMedia = FALSE;\r
+ Media->MediaPresent = TRUE;\r
+ Media->ReadOnly = FALSE;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate device information data structure to contain device information.\r
+ And insert the data structure to the tail of device list for tracing.\r
+\r
+ @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+ instance.\r
+ @param[in] DeviceIndex The device index.\r
+ @param[in] Port The port number of the ATA device to send\r
+ the command.\r
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA\r
+ device to send the command.\r
+ If there is no port multiplier, then specify\r
+ 0xFFFF.\r
+ @param[in] FisIndex The index of the FIS of the ATA device to\r
+ send the command.\r
+ @param[in] IdentifyData The data buffer to store the output of the\r
+ IDENTIFY command.\r
+\r
+ @retval EFI_SUCCESS Successfully insert the ATA device to the\r
+ tail of device list.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough resource.\r
+\r
+**/\r
+EFI_STATUS\r
+CreateNewDevice (\r
+ IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINTN DeviceIndex,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplier,\r
+ IN UINT8 FisIndex,\r
+ IN ATA_IDENTIFY_DATA *IdentifyData\r
+ )\r
+{\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ EFI_STATUS Status;\r
+\r
+ DeviceData = AllocateZeroPool (sizeof (PEI_AHCI_ATA_DEVICE_DATA));\r
+ if (DeviceData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (IdentifyData != NULL) {\r
+ DeviceData->IdentifyData = AllocateCopyPool (sizeof (ATA_IDENTIFY_DATA), IdentifyData);\r
+ if (DeviceData->IdentifyData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ }\r
+\r
+ DeviceData->Signature = AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE;\r
+ DeviceData->Port = Port;\r
+ DeviceData->PortMultiplier = PortMultiplier;\r
+ DeviceData->FisIndex = FisIndex;\r
+ DeviceData->DeviceIndex = DeviceIndex;\r
+ DeviceData->Private = Private;\r
+\r
+ Status = IdentifyAtaDevice (DeviceData);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (DeviceData->TrustComputing) {\r
+ Private->TrustComputingDevices++;\r
+ DeviceData->TrustComputingDeviceIndex = Private->TrustComputingDevices;\r
+ }\r
+ Private->ActiveDevices++;\r
+ InsertTailList (&Private->DeviceList, &DeviceData->Link);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Initialize ATA host controller at AHCI mode.\r
+\r
+ The function is designed to initialize ATA host controller.\r
+\r
+ @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
+\r
+ @retval EFI_SUCCESS The ATA AHCI controller is initialized successfully.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to complete while initializing\r
+ the controller.\r
+ @retval Others A device error occurred while initializing the\r
+ controller.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciModeInitialization (\r
+ IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN AhciBar;\r
+ UINT32 Capability;\r
+ UINT32 Value;\r
+ UINT8 MaxPortNumber;\r
+ UINT32 PortImplementBitMap;\r
+ UINT32 PortInitializeBitMap;\r
+ EFI_AHCI_REGISTERS *AhciRegisters;\r
+ UINT8 PortIndex;\r
+ UINT8 Port;\r
+ DATA_64 Data64;\r
+ UINT32 Data;\r
+ UINT32 Offset;\r
+ UINT32 PhyDetectDelay;\r
+ UINTN DeviceIndex;\r
+ ATA_IDENTIFY_DATA IdentifyData;\r
+\r
+ AhciBar = Private->MmioBase;\r
+\r
+ Status = AhciReset (AhciBar, AHCI_PEI_RESET_TIMEOUT);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: AHCI HBA reset failed with %r.\n", __FUNCTION__, Status));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Collect AHCI controller information\r
+ //\r
+ Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
+\r
+ //\r
+ // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
+ //\r
+ Value = AhciReadReg (AhciBar, AHCI_GHC_OFFSET);\r
+ if ((Value & AHCI_GHC_ENABLE) == 0) {\r
+ AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
+ }\r
+\r
+ Status = AhciCreateTransferDescriptor (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Transfer-related data allocation failed with %r.\n",\r
+ __FUNCTION__, Status\r
+ ));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Get the number of command slots per port supported by this HBA.\r
+ //\r
+ MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
+\r
+ //\r
+ // Get the bit map of those ports exposed by this HBA.\r
+ // It indicates which ports that the HBA supports are available for software\r
+ // to use.\r
+ //\r
+ PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
+\r
+ //\r
+ // Get the number of ports that actually needed to be initialized.\r
+ //\r
+ MaxPortNumber = MIN (MaxPortNumber, (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1));\r
+ MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
+\r
+ PortInitializeBitMap = Private->PortBitMap;\r
+ AhciRegisters = &Private->AhciRegisters;\r
+ DeviceIndex = 0;\r
+ //\r
+ // Enumerate ATA ports\r
+ //\r
+ for (PortIndex = 1; PortIndex <= MaxPortNumber; PortIndex ++) {\r
+ Status = AhciGetPortFromMap (PortInitializeBitMap, PortIndex, &Port);\r
+ if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
+ //\r
+ // Initialize FIS Base Address Register and Command List Base Address\r
+ // Register for use.\r
+ //\r
+ Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) +\r
+ sizeof (EFI_AHCI_RECEIVED_FIS) * (PortIndex - 1);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+ Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
+ AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
+\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ Data = AhciReadReg (AhciBar, Offset);\r
+ if ((Data & AHCI_PORT_CMD_CPD) != 0) {\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_POD);\r
+ }\r
+\r
+ if ((Capability & AHCI_CAP_SSS) != 0) {\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_SUD);\r
+ }\r
+\r
+ //\r
+ // Disable aggressive power management.\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SCTL;\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_SCTL_IPM_INIT);\r
+ //\r
+ // Disable the reporting of the corresponding interrupt to system software.\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IE;\r
+ AhciAndReg (AhciBar, Offset, 0);\r
+\r
+ //\r
+ // Enable FIS Receive DMA engine for the first D2H FIS.\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
+\r
+ //\r
+ // Wait no longer than 15 ms to wait the Phy to detect the presence of a device.\r
+ //\r
+ PhyDetectDelay = AHCI_BUS_PHY_DETECT_TIMEOUT;\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SSTS;\r
+ do {\r
+ Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_SSTS_DET_MASK;\r
+ if ((Data == AHCI_PORT_SSTS_DET_PCE) || (Data == AHCI_PORT_SSTS_DET)) {\r
+ break;\r
+ }\r
+\r
+ MicroSecondDelay (1000);\r
+ PhyDetectDelay--;\r
+ } while (PhyDetectDelay > 0);\r
+\r
+ if (PhyDetectDelay == 0) {\r
+ //\r
+ // No device detected at this port.\r
+ // Clear PxCMD.SUD for those ports at which there are no device present.\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
+ AhciAndReg (AhciBar, Offset, (UINT32) ~(AHCI_PORT_CMD_SUD));\r
+ DEBUG ((DEBUG_ERROR, "%a: No device detected at Port %d.\n", __FUNCTION__, Port));\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
+ // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
+ //\r
+ PhyDetectDelay = 16 * 1000;\r
+ do {\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
+ if (AhciReadReg(AhciBar, Offset) != 0) {\r
+ AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
+ }\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
+\r
+ Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_TFD_MASK;\r
+ if (Data == 0) {\r
+ break;\r
+ }\r
+\r
+ MicroSecondDelay (1000);\r
+ PhyDetectDelay--;\r
+ } while (PhyDetectDelay > 0);\r
+\r
+ if (PhyDetectDelay == 0) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Port %d device presence detected but phy not ready (TFD=0x%x).\n",\r
+ __FUNCTION__, Port, Data\r
+ ));\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // When the first D2H register FIS is received, the content of PxSIG register is updated.\r
+ //\r
+ Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SIG;\r
+ Status = AhciWaitMmioSet (\r
+ AhciBar,\r
+ Offset,\r
+ 0x0000FFFF,\r
+ 0x00000101,\r
+ 160000000\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Error occured when waiting for the first D2H register FIS - %r\n",\r
+ __FUNCTION__, Status\r
+ ));\r
+ continue;\r
+ }\r
+\r
+ Data = AhciReadReg (AhciBar, Offset);\r
+ if ((Data & AHCI_ATAPI_SIG_MASK) == AHCI_ATA_DEVICE_SIG) {\r
+ Status = AhciIdentify (Private, Port, 0, PortIndex - 1, &IdentifyData);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: AhciIdentify() failed with %r\n", __FUNCTION__, Status));\r
+ continue;\r
+ }\r
+ DEBUG ((DEBUG_INFO, "%a: ATA hard disk found on Port %d.\n", __FUNCTION__, Port));\r
+ } else {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Found an ATA hard disk device, add it into the device list.\r
+ //\r
+ DeviceIndex++;\r
+ CreateNewDevice (\r
+ Private,\r
+ DeviceIndex,\r
+ Port,\r
+ 0xFFFF,\r
+ PortIndex - 1,\r
+ &IdentifyData\r
+ );\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Trust transfer data from/to ATA device.\r
+\r
+ This function performs one ATA pass through transaction to do a trust transfer\r
+ from/to ATA device. It chooses the appropriate ATA command and protocol to invoke\r
+ PassThru interface of ATA pass through.\r
+\r
+ @param[in] DeviceData Pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
+ @param[in,out] Buffer The pointer to the current transaction buffer.\r
+ @param[in] SecurityProtocolId\r
+ The value of the "Security Protocol" parameter\r
+ of the security protocol command to be sent.\r
+ @param[in] SecurityProtocolSpecificData\r
+ The value of the "Security Protocol Specific"\r
+ parameter of the security protocol command to\r
+ be sent.\r
+ @param[in] TransferLength The block number or sector count of the transfer.\r
+ @param[in] IsTrustSend Indicates whether it is a trust send operation\r
+ or not.\r
+ @param[in] Timeout The timeout, in 100ns units, to use for the execution\r
+ of the security protocol command. A Timeout value\r
+ of 0 means that this function will wait indefinitely\r
+ for the security protocol command to execute. If\r
+ Timeout is greater than zero, then this function\r
+ will return EFI_TIMEOUT if the time required to\r
+ execute the receive data command is greater than\r
+ Timeout.\r
+ @param[out] TransferLengthOut\r
+ A pointer to a buffer to store the size in bytes\r
+ of the data written to the buffer. Ignore it when\r
+ IsTrustSend is TRUE.\r
+\r
+ @retval EFI_SUCCESS The data transfer is complete successfully.\r
+ @return others Some error occurs when transferring data.\r
+\r
+**/\r
+EFI_STATUS\r
+TrustTransferAtaDevice (\r
+ IN PEI_AHCI_ATA_DEVICE_DATA *DeviceData,\r
+ IN OUT VOID *Buffer,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN TransferLength,\r
+ IN BOOLEAN IsTrustSend,\r
+ IN UINT64 Timeout,\r
+ OUT UINTN *TransferLengthOut\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru;\r
+ EFI_ATA_COMMAND_BLOCK Acb;\r
+ EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;\r
+ EFI_STATUS Status;\r
+ VOID *NewBuffer;\r
+\r
+ Private = DeviceData->Private;\r
+ AtaPassThru = &Private->AtaPassThruPpi;\r
+\r
+ //\r
+ // Ensure IsTrustSend are valid boolean values\r
+ //\r
+ ASSERT ((UINTN) IsTrustSend < 2);\r
+ if ((UINTN) IsTrustSend >= 2) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Prepare for ATA command block.\r
+ //\r
+ ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+ if (TransferLength == 0) {\r
+ Acb.AtaCommand = ATA_CMD_TRUST_NON_DATA;\r
+ } else {\r
+ Acb.AtaCommand = mAtaTrustCommands[IsTrustSend];\r
+ }\r
+ Acb.AtaFeatures = SecurityProtocolId;\r
+ Acb.AtaSectorCount = (UINT8) (TransferLength / 512);\r
+ Acb.AtaSectorNumber = (UINT8) ((TransferLength / 512) >> 8);\r
+ //\r
+ // NOTE: ATA Spec has no explicitly definition for Security Protocol Specific layout.\r
+ // Here use big endian for Cylinder register.\r
+ //\r
+ Acb.AtaCylinderHigh = (UINT8) SecurityProtocolSpecificData;\r
+ Acb.AtaCylinderLow = (UINT8) (SecurityProtocolSpecificData >> 8);\r
+ Acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 |\r
+ (DeviceData->PortMultiplier == 0xFFFF ?\r
+ 0 : (DeviceData->PortMultiplier << 4)));\r
+\r
+ //\r
+ // Prepare for ATA pass through packet.\r
+ //\r
+ ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));\r
+ if (TransferLength == 0) {\r
+ Packet.InTransferLength = 0;\r
+ Packet.OutTransferLength = 0;\r
+ Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;\r
+ } else if (IsTrustSend) {\r
+ //\r
+ // Check the alignment of the incoming buffer prior to invoking underlying\r
+ // ATA PassThru PPI.\r
+ //\r
+ if ((AtaPassThru->Mode->IoAlign > 1) &&\r
+ !IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) {\r
+ NewBuffer = AllocateAlignedPages (\r
+ EFI_SIZE_TO_PAGES (TransferLength),\r
+ AtaPassThru->Mode->IoAlign\r
+ );\r
+ if (NewBuffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CopyMem (NewBuffer, Buffer, TransferLength);\r
+ Buffer = NewBuffer;\r
+ }\r
+ Packet.OutDataBuffer = Buffer;\r
+ Packet.OutTransferLength = (UINT32) TransferLength;\r
+ Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
+ } else {\r
+ Packet.InDataBuffer = Buffer;\r
+ Packet.InTransferLength = (UINT32) TransferLength;\r
+ Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
+ }\r
+ Packet.Asb = NULL;\r
+ Packet.Acb = &Acb;\r
+ Packet.Timeout = Timeout;\r
+ Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;\r
+\r
+ Status = AtaPassThru->PassThru (\r
+ AtaPassThru,\r
+ DeviceData->Port,\r
+ DeviceData->PortMultiplier,\r
+ &Packet\r
+ );\r
+ if (TransferLengthOut != NULL) {\r
+ if (!IsTrustSend) {\r
+ *TransferLengthOut = Packet.InTransferLength;\r
+ }\r
+ }\r
+ return Status;\r
+}\r