--- /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
--- /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
+EFI_PEI_PPI_DESCRIPTOR mAhciAtaPassThruPpiListTemplate = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEdkiiPeiAtaPassThruPpiGuid,\r
+ NULL\r
+};\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mAhciStorageSecurityPpiListTemplate = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEdkiiPeiStorageSecurityCommandPpiGuid,\r
+ NULL\r
+};\r
+\r
+EFI_PEI_NOTIFY_DESCRIPTOR mAhciEndOfPeiNotifyListTemplate = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEfiEndOfPeiSignalPpiGuid,\r
+ AhciPeimEndOfPei\r
+};\r
+\r
+\r
+/**\r
+ Free the DMA resources allocated by an ATA AHCI controller.\r
+\r
+ @param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA data\r
+ structure.\r
+\r
+**/\r
+VOID\r
+AhciFreeDmaResource (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
+ )\r
+{\r
+ EFI_AHCI_REGISTERS *AhciRegisters;\r
+\r
+ ASSERT (Private != NULL);\r
+\r
+ AhciRegisters = &Private->AhciRegisters;\r
+\r
+ if (AhciRegisters->AhciRFisMap != NULL) {\r
+ IoMmuFreeBuffer (\r
+ EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),\r
+ AhciRegisters->AhciRFis,\r
+ AhciRegisters->AhciRFisMap\r
+ );\r
+ }\r
+\r
+ if (AhciRegisters->AhciCmdListMap != NULL) {\r
+ IoMmuFreeBuffer (\r
+ EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),\r
+ AhciRegisters->AhciCmdList,\r
+ AhciRegisters->AhciCmdListMap\r
+ );\r
+ }\r
+\r
+ if (AhciRegisters->AhciCmdTableMap != NULL) {\r
+ IoMmuFreeBuffer (\r
+ EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdTableSize),\r
+ AhciRegisters->AhciCmdTable,\r
+ AhciRegisters->AhciCmdTableMap\r
+ );\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ One notified function to cleanup the allocated DMA buffers at EndOfPei.\r
+\r
+ @param[in] PeiServices Pointer to PEI Services Table.\r
+ @param[in] NotifyDescriptor Pointer to the descriptor for the Notification\r
+ event that caused this function to execute.\r
+ @param[in] Ppi Pointer to the PPI data associated with this function.\r
+\r
+ @retval EFI_SUCCESS The function completes successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciPeimEndOfPei (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,\r
+ IN VOID *Ppi\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);\r
+ AhciFreeDmaResource (Private);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Entry point of the PEIM.\r
+\r
+ @param[in] FileHandle Handle of the file being invoked.\r
+ @param[in] PeiServices Describes the list of possible PEI Services.\r
+\r
+ @retval EFI_SUCCESS PPI successfully installed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaAhciPeimEntry (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MODE BootMode;\r
+ EDKII_ATA_AHCI_HOST_CONTROLLER_PPI *AhciHcPpi;\r
+ UINT8 Controller;\r
+ UINTN MmioBase;\r
+ UINTN DevicePathLength;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ UINT32 PortBitMap;\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ UINT8 NumberOfPorts;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));\r
+\r
+ //\r
+ // Get the current boot mode.\r
+ //\r
+ Status = PeiServicesGetBootMode (&BootMode);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Locate the ATA AHCI host controller PPI.\r
+ //\r
+ Status = PeiServicesLocatePpi (\r
+ &gEdkiiPeiAtaAhciHostControllerPpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **) &AhciHcPpi\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: Failed to locate AtaAhciHostControllerPpi.\n", __FUNCTION__));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Controller = 0;\r
+ MmioBase = 0;\r
+ while (TRUE) {\r
+ Status = AhciHcPpi->GetAhciHcMmioBar (\r
+ AhciHcPpi,\r
+ Controller,\r
+ &MmioBase\r
+ );\r
+ //\r
+ // When status is error, meant no controller is found.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ Status = AhciHcPpi->GetAhciHcDevicePath (\r
+ AhciHcPpi,\r
+ Controller,\r
+ &DevicePathLength,\r
+ &DevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR, "%a: Fail to allocate get the device path for Controller %d.\n",\r
+ __FUNCTION__, Controller\r
+ ));\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check validity of the device path of the ATA AHCI controller.\r
+ //\r
+ Status = AhciIsHcDevicePathValid (DevicePath, DevicePathLength);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",\r
+ __FUNCTION__, Controller\r
+ ));\r
+ Controller++;\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // For S3 resume performance consideration, not all ports on an ATA AHCI\r
+ // controller will be enumerated/initialized. The driver consumes the\r
+ // content within S3StorageDeviceInitList LockBox to get the ports that\r
+ // will be enumerated/initialized during S3 resume.\r
+ //\r
+ if (BootMode == BOOT_ON_S3_RESUME) {\r
+ NumberOfPorts = AhciS3GetEumeratePorts (DevicePath, DevicePathLength, &PortBitMap);\r
+ if (NumberOfPorts == 0) {\r
+ //\r
+ // No ports need to be enumerated for this controller.\r
+ //\r
+ Controller++;\r
+ continue;\r
+ }\r
+ } else {\r
+ PortBitMap = MAX_UINT32;\r
+ }\r
+\r
+ //\r
+ // Memory allocation for controller private data.\r
+ //\r
+ Private = AllocateZeroPool (sizeof (PEI_AHCI_CONTROLLER_PRIVATE_DATA));\r
+ if (Private == NULL) {\r
+ DEBUG ((\r
+ DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",\r
+ __FUNCTION__, Controller\r
+ ));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Initialize controller private data.\r
+ //\r
+ Private->Signature = AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;\r
+ Private->MmioBase = MmioBase;\r
+ Private->DevicePathLength = DevicePathLength;\r
+ Private->DevicePath = DevicePath;\r
+ Private->PortBitMap = PortBitMap;\r
+ InitializeListHead (&Private->DeviceList);\r
+\r
+ Status = AhciModeInitialization (Private);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Controller initialization fail for Controller %d with Status - %r.\n",\r
+ __FUNCTION__,\r
+ Controller,\r
+ Status\r
+ ));\r
+ Controller++;\r
+ continue;\r
+ }\r
+\r
+ Private->AtaPassThruMode.Attributes = EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
+ EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL;\r
+ Private->AtaPassThruMode.IoAlign = sizeof (UINTN);\r
+ Private->AtaPassThruPpi.Revision = EDKII_PEI_ATA_PASS_THRU_PPI_REVISION;\r
+ Private->AtaPassThruPpi.Mode = &Private->AtaPassThruMode;\r
+ Private->AtaPassThruPpi.PassThru = AhciAtaPassThruPassThru;\r
+ Private->AtaPassThruPpi.GetNextPort = AhciAtaPassThruGetNextPort;\r
+ Private->AtaPassThruPpi.GetNextDevice = AhciAtaPassThruGetNextDevice;\r
+ Private->AtaPassThruPpi.GetDevicePath = AhciAtaPassThruGetDevicePath;\r
+ CopyMem (\r
+ &Private->AtaPassThruPpiList,\r
+ &mAhciAtaPassThruPpiListTemplate,\r
+ sizeof (EFI_PEI_PPI_DESCRIPTOR)\r
+ );\r
+ Private->AtaPassThruPpiList.Ppi = &Private->AtaPassThruPpi;\r
+ PeiServicesInstallPpi (&Private->AtaPassThruPpiList);\r
+\r
+ if (Private->TrustComputingDevices != 0) {\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%a: Security Security Command PPI will be produced for Controller %d.\n",\r
+ __FUNCTION__, Controller\r
+ ));\r
+ Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION;\r
+ Private->StorageSecurityPpi.GetNumberofDevices = AhciStorageSecurityGetDeviceNo;\r
+ Private->StorageSecurityPpi.GetDevicePath = AhciStorageSecurityGetDevicePath;\r
+ Private->StorageSecurityPpi.ReceiveData = AhciStorageSecurityReceiveData;\r
+ Private->StorageSecurityPpi.SendData = AhciStorageSecuritySendData;\r
+ CopyMem (\r
+ &Private->StorageSecurityPpiList,\r
+ &mAhciStorageSecurityPpiListTemplate,\r
+ sizeof (EFI_PEI_PPI_DESCRIPTOR)\r
+ );\r
+ Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;\r
+ PeiServicesInstallPpi (&Private->StorageSecurityPpiList);\r
+ }\r
+\r
+ CopyMem (\r
+ &Private->EndOfPeiNotifyList,\r
+ &mAhciEndOfPeiNotifyListTemplate,\r
+ sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)\r
+ );\r
+ PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",\r
+ __FUNCTION__, Controller\r
+ ));\r
+ Controller++;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /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
+#ifndef _AHCI_PEI_H_\r
+#define _AHCI_PEI_H_\r
+\r
+#include <PiPei.h>\r
+\r
+#include <IndustryStandard/Atapi.h>\r
+\r
+#include <Ppi/AtaAhciController.h>\r
+#include <Ppi/IoMmu.h>\r
+#include <Ppi/EndOfPeiPhase.h>\r
+#include <Ppi/AtaPassThru.h>\r
+#include <Ppi/BlockIo2.h>\r
+#include <Ppi/StorageSecurityCommand.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/TimerLib.h>\r
+\r
+//\r
+// Structure forward declarations\r
+//\r
+typedef struct _PEI_AHCI_CONTROLLER_PRIVATE_DATA PEI_AHCI_CONTROLLER_PRIVATE_DATA;\r
+\r
+#include "AhciPeiPassThru.h"\r
+#include "AhciPeiStorageSecurity.h"\r
+\r
+//\r
+// ATA AHCI driver implementation related definitions\r
+//\r
+//\r
+// Refer SATA1.0a spec section 5.2, the Phy detection time should be less than 10ms.\r
+// The value is in millisecond units. Add a bit of margin for robustness.\r
+//\r
+#define AHCI_BUS_PHY_DETECT_TIMEOUT 15\r
+//\r
+// Refer SATA1.0a spec, the bus reset time should be less than 1s.\r
+// The value is in 100ns units.\r
+//\r
+#define AHCI_PEI_RESET_TIMEOUT 10000000\r
+//\r
+// Time out Value for ATA pass through protocol, in 100ns units.\r
+//\r
+#define ATA_TIMEOUT 30000000\r
+//\r
+// Maximal number of Physical Region Descriptor Table entries supported.\r
+//\r
+#define AHCI_MAX_PRDT_NUMBER 8\r
+\r
+#define AHCI_CAPABILITY_OFFSET 0x0000\r
+#define AHCI_CAP_SAM BIT18\r
+#define AHCI_CAP_SSS BIT27\r
+\r
+#define AHCI_GHC_OFFSET 0x0004\r
+#define AHCI_GHC_RESET BIT0\r
+#define AHCI_GHC_ENABLE BIT31\r
+\r
+#define AHCI_IS_OFFSET 0x0008\r
+#define AHCI_PI_OFFSET 0x000C\r
+\r
+#define AHCI_MAX_PORTS 32\r
+\r
+typedef struct {\r
+ UINT32 Lower32;\r
+ UINT32 Upper32;\r
+} DATA_32;\r
+\r
+typedef union {\r
+ DATA_32 Uint32;\r
+ UINT64 Uint64;\r
+} DATA_64;\r
+\r
+#define AHCI_ATAPI_SIG_MASK 0xFFFF0000\r
+#define AHCI_ATA_DEVICE_SIG 0x00000000\r
+\r
+//\r
+// Each PRDT entry can point to a memory block up to 4M byte\r
+//\r
+#define AHCI_MAX_DATA_PER_PRDT 0x400000\r
+\r
+#define AHCI_FIS_REGISTER_H2D 0x27 //Register FIS - Host to Device\r
+#define AHCI_FIS_REGISTER_H2D_LENGTH 20\r
+#define AHCI_FIS_REGISTER_D2H 0x34 //Register FIS - Device to Host\r
+#define AHCI_FIS_PIO_SETUP 0x5F //PIO Setup FIS - Device to Host\r
+\r
+#define AHCI_D2H_FIS_OFFSET 0x40\r
+#define AHCI_PIO_FIS_OFFSET 0x20\r
+#define AHCI_FIS_TYPE_MASK 0xFF\r
+\r
+//\r
+// Port register\r
+//\r
+#define AHCI_PORT_START 0x0100\r
+#define AHCI_PORT_REG_WIDTH 0x0080\r
+#define AHCI_PORT_CLB 0x0000\r
+#define AHCI_PORT_CLBU 0x0004\r
+#define AHCI_PORT_FB 0x0008\r
+#define AHCI_PORT_FBU 0x000C\r
+#define AHCI_PORT_IS 0x0010\r
+#define AHCI_PORT_IE 0x0014\r
+#define AHCI_PORT_CMD 0x0018\r
+#define AHCI_PORT_CMD_ST BIT0\r
+#define AHCI_PORT_CMD_SUD BIT1\r
+#define AHCI_PORT_CMD_POD BIT2\r
+#define AHCI_PORT_CMD_CLO BIT3\r
+#define AHCI_PORT_CMD_FRE BIT4\r
+#define AHCI_PORT_CMD_FR BIT14\r
+#define AHCI_PORT_CMD_CR BIT15\r
+#define AHCI_PORT_CMD_CPD BIT20\r
+#define AHCI_PORT_CMD_ATAPI BIT24\r
+#define AHCI_PORT_CMD_DLAE BIT25\r
+#define AHCI_PORT_CMD_ALPE BIT26\r
+#define AHCI_PORT_CMD_ACTIVE (1 << 28)\r
+#define AHCI_PORT_CMD_ICC_MASK (BIT28 | BIT29 | BIT30 | BIT31)\r
+\r
+#define AHCI_PORT_TFD 0x0020\r
+#define AHCI_PORT_TFD_ERR BIT0\r
+#define AHCI_PORT_TFD_DRQ BIT3\r
+#define AHCI_PORT_TFD_BSY BIT7\r
+#define AHCI_PORT_TFD_MASK (BIT7 | BIT3 | BIT0)\r
+\r
+#define AHCI_PORT_SIG 0x0024\r
+#define AHCI_PORT_SSTS 0x0028\r
+#define AHCI_PORT_SSTS_DET_MASK 0x000F\r
+#define AHCI_PORT_SSTS_DET 0x0001\r
+#define AHCI_PORT_SSTS_DET_PCE 0x0003\r
+\r
+#define AHCI_PORT_SCTL 0x002C\r
+#define AHCI_PORT_SCTL_IPM_INIT 0x0300\r
+\r
+#define AHCI_PORT_SERR 0x0030\r
+#define AHCI_PORT_CI 0x0038\r
+\r
+#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0)\r
+#define TIMER_PERIOD_SECONDS(Seconds) MultU64x32((UINT64)(Seconds), 10000000)\r
+\r
+#pragma pack(1)\r
+\r
+//\r
+// Received FIS structure\r
+//\r
+typedef struct {\r
+ UINT8 AhciDmaSetupFis[0x1C]; // Dma Setup Fis: offset 0x00\r
+ UINT8 AhciDmaSetupFisRsvd[0x04];\r
+ UINT8 AhciPioSetupFis[0x14]; // Pio Setup Fis: offset 0x20\r
+ UINT8 AhciPioSetupFisRsvd[0x0C];\r
+ UINT8 AhciD2HRegisterFis[0x14]; // D2H Register Fis: offset 0x40\r
+ UINT8 AhciD2HRegisterFisRsvd[0x04];\r
+ UINT64 AhciSetDeviceBitsFis; // Set Device Bits Fix: offset 0x58\r
+ UINT8 AhciUnknownFis[0x40]; // Unkonwn Fis: offset 0x60\r
+ UINT8 AhciUnknownFisRsvd[0x60];\r
+} EFI_AHCI_RECEIVED_FIS;\r
+\r
+//\r
+// Command List structure includes total 32 entries.\r
+// The entry Data structure is listed at the following.\r
+//\r
+typedef struct {\r
+ UINT32 AhciCmdCfl:5; //Command FIS Length\r
+ UINT32 AhciCmdA:1; //ATAPI\r
+ UINT32 AhciCmdW:1; //Write\r
+ UINT32 AhciCmdP:1; //Prefetchable\r
+ UINT32 AhciCmdR:1; //Reset\r
+ UINT32 AhciCmdB:1; //BIST\r
+ UINT32 AhciCmdC:1; //Clear Busy upon R_OK\r
+ UINT32 AhciCmdRsvd:1;\r
+ UINT32 AhciCmdPmp:4; //Port Multiplier Port\r
+ UINT32 AhciCmdPrdtl:16; //Physical Region Descriptor Table Length\r
+ UINT32 AhciCmdPrdbc; //Physical Region Descriptor Byte Count\r
+ UINT32 AhciCmdCtba; //Command Table Descriptor Base Address\r
+ UINT32 AhciCmdCtbau; //Command Table Descriptor Base Address Upper 32-BITs\r
+ UINT32 AhciCmdRsvd1[4];\r
+} EFI_AHCI_COMMAND_LIST;\r
+\r
+//\r
+// This is a software constructed FIS.\r
+// For Data transfer operations, this is the H2D Register FIS format as\r
+// specified in the Serial ATA Revision 2.6 specification.\r
+//\r
+typedef struct {\r
+ UINT8 AhciCFisType;\r
+ UINT8 AhciCFisPmNum:4;\r
+ UINT8 AhciCFisRsvd:1;\r
+ UINT8 AhciCFisRsvd1:1;\r
+ UINT8 AhciCFisRsvd2:1;\r
+ UINT8 AhciCFisCmdInd:1;\r
+ UINT8 AhciCFisCmd;\r
+ UINT8 AhciCFisFeature;\r
+ UINT8 AhciCFisSecNum;\r
+ UINT8 AhciCFisClyLow;\r
+ UINT8 AhciCFisClyHigh;\r
+ UINT8 AhciCFisDevHead;\r
+ UINT8 AhciCFisSecNumExp;\r
+ UINT8 AhciCFisClyLowExp;\r
+ UINT8 AhciCFisClyHighExp;\r
+ UINT8 AhciCFisFeatureExp;\r
+ UINT8 AhciCFisSecCount;\r
+ UINT8 AhciCFisSecCountExp;\r
+ UINT8 AhciCFisRsvd3;\r
+ UINT8 AhciCFisControl;\r
+ UINT8 AhciCFisRsvd4[4];\r
+ UINT8 AhciCFisRsvd5[44];\r
+} EFI_AHCI_COMMAND_FIS;\r
+\r
+//\r
+// ACMD: ATAPI command (12 or 16 bytes)\r
+//\r
+typedef struct {\r
+ UINT8 AtapiCmd[0x10];\r
+} EFI_AHCI_ATAPI_COMMAND;\r
+\r
+//\r
+// Physical Region Descriptor Table includes up to 65535 entries\r
+// The entry data structure is listed at the following.\r
+// the actual entry number comes from the PRDTL field in the command\r
+// list entry for this command slot.\r
+//\r
+typedef struct {\r
+ UINT32 AhciPrdtDba; //Data Base Address\r
+ UINT32 AhciPrdtDbau; //Data Base Address Upper 32-BITs\r
+ UINT32 AhciPrdtRsvd;\r
+ UINT32 AhciPrdtDbc:22; //Data Byte Count\r
+ UINT32 AhciPrdtRsvd1:9;\r
+ UINT32 AhciPrdtIoc:1; //Interrupt on Completion\r
+} EFI_AHCI_COMMAND_PRDT;\r
+\r
+//\r
+// Command table Data strucute which is pointed to by the entry in the command list\r
+//\r
+typedef struct {\r
+ EFI_AHCI_COMMAND_FIS CommandFis; // A software constructed FIS.\r
+ EFI_AHCI_ATAPI_COMMAND AtapiCmd; // 12 or 16 bytes ATAPI cmd.\r
+ UINT8 Reserved[0x30];\r
+ //\r
+ // The scatter/gather list for Data transfer.\r
+ //\r
+ EFI_AHCI_COMMAND_PRDT PrdtTable[AHCI_MAX_PRDT_NUMBER];\r
+} EFI_AHCI_COMMAND_TABLE;\r
+\r
+#pragma pack()\r
+\r
+typedef struct {\r
+ EFI_AHCI_RECEIVED_FIS *AhciRFis;\r
+ EFI_AHCI_COMMAND_LIST *AhciCmdList;\r
+ EFI_AHCI_COMMAND_TABLE *AhciCmdTable;\r
+ UINTN MaxRFisSize;\r
+ UINTN MaxCmdListSize;\r
+ UINTN MaxCmdTableSize;\r
+ VOID *AhciRFisMap;\r
+ VOID *AhciCmdListMap;\r
+ VOID *AhciCmdTableMap;\r
+} EFI_AHCI_REGISTERS;\r
+\r
+//\r
+// Unique signature for AHCI ATA device information structure.\r
+//\r
+#define AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE SIGNATURE_32 ('A', 'P', 'A', 'D')\r
+\r
+//\r
+// AHCI mode device information structure.\r
+//\r
+typedef struct {\r
+ UINT32 Signature;\r
+ LIST_ENTRY Link;\r
+\r
+ UINT16 Port;\r
+ UINT16 PortMultiplier;\r
+ UINT8 FisIndex;\r
+ UINTN DeviceIndex;\r
+ ATA_IDENTIFY_DATA *IdentifyData;\r
+\r
+ BOOLEAN Lba48Bit;\r
+ BOOLEAN TrustComputing;\r
+ UINTN TrustComputingDeviceIndex;\r
+ EFI_PEI_BLOCK_IO2_MEDIA Media;\r
+\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+} PEI_AHCI_ATA_DEVICE_DATA;\r
+\r
+#define AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS(a) \\r
+ CR (a, \\r
+ PEI_AHCI_ATA_DEVICE_DATA, \\r
+ Link, \\r
+ AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE \\r
+ );\r
+\r
+//\r
+// Unique signature for private data structure.\r
+//\r
+#define AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('A','P','C','P')\r
+\r
+//\r
+// ATA AHCI controller private data structure.\r
+//\r
+struct _PEI_AHCI_CONTROLLER_PRIVATE_DATA {\r
+ UINT32 Signature;\r
+ UINTN MmioBase;\r
+ UINTN DevicePathLength;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+\r
+ EFI_ATA_PASS_THRU_MODE AtaPassThruMode;\r
+ EDKII_PEI_ATA_PASS_THRU_PPI AtaPassThruPpi;\r
+ EDKII_PEI_STORAGE_SECURITY_CMD_PPI StorageSecurityPpi;\r
+ EFI_PEI_PPI_DESCRIPTOR AtaPassThruPpiList;\r
+ EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList;\r
+ EFI_PEI_PPI_DESCRIPTOR BlkIo2PpiList;\r
+ EFI_PEI_PPI_DESCRIPTOR StorageSecurityPpiList;\r
+ EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList;\r
+\r
+ EFI_AHCI_REGISTERS AhciRegisters;\r
+\r
+ UINT32 PortBitMap;\r
+ UINT32 ActiveDevices;\r
+ UINT32 TrustComputingDevices;\r
+ LIST_ENTRY DeviceList;\r
+\r
+ UINT16 PreviousPort;\r
+ UINT16 PreviousPortMultiplier;\r
+};\r
+\r
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU(a) \\r
+ CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, AtaPassThruPpi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO(a) \\r
+ CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, BlkIoPpi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2(a) \\r
+ CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, BlkIo2Ppi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY(a) \\r
+ CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, StorageSecurityPpi, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+#define GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a) \\r
+ CR (a, PEI_AHCI_CONTROLLER_PRIVATE_DATA, EndOfPeiNotifyList, AHCI_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE)\r
+\r
+//\r
+// Global variables\r
+//\r
+extern UINT32 mMaxTransferBlockNumber[2];\r
+\r
+//\r
+// Internal functions\r
+//\r
+\r
+/**\r
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+ OperationBusMasterCommonBuffer64 mapping.\r
+\r
+ @param Pages The number of pages to allocate.\r
+ @param HostAddress A pointer to store the base system memory address of the\r
+ allocated range.\r
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
+ access the hosts HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were allocated.\r
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuAllocateBuffer (\r
+ IN UINTN Pages,\r
+ OUT VOID **HostAddress,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ );\r
+\r
+/**\r
+ Frees memory that was allocated with AllocateBuffer().\r
+\r
+ @param Pages The number of pages to free.\r
+ @param HostAddress The base system memory address of the allocated range.\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were freed.\r
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+ was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuFreeBuffer (\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress,\r
+ IN VOID *Mapping\r
+ );\r
+\r
+/**\r
+ Provides the controller-specific addresses required to access system memory from a\r
+ DMA bus master.\r
+\r
+ @param Operation Indicates if the bus master is going to read or write to system memory.\r
+ @param HostAddress The system memory address to map to the PCI controller.\r
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
+ that were mapped.\r
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
+ access the hosts HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuMap (\r
+ IN EDKII_IOMMU_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ );\r
+\r
+/**\r
+ Completes the Map() operation and releases any corresponding resources.\r
+\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The range was unmapped.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+IoMmuUnmap (\r
+ IN VOID *Mapping\r
+ );\r
+\r
+/**\r
+ One notified function to cleanup the allocated DMA buffers at EndOfPei.\r
+\r
+ @param[in] PeiServices Pointer to PEI Services Table.\r
+ @param[in] NotifyDescriptor Pointer to the descriptor for the Notification\r
+ event that caused this function to execute.\r
+ @param[in] Ppi Pointer to the PPI data associated with this function.\r
+\r
+ @retval EFI_SUCCESS The function completes successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciPeimEndOfPei (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,\r
+ IN VOID *Ppi\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
+/**\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
+/**\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
+/**\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
+/**\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
+/**\r
+ Returns a pointer to the next node in a device path.\r
+\r
+ If Node is NULL, then ASSERT().\r
+\r
+ @param Node A pointer to a device path node data structure.\r
+\r
+ @return a pointer to the device path node that follows the device path node\r
+ specified by Node.\r
+\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+NextDevicePathNode (\r
+ IN CONST VOID *Node\r
+ );\r
+\r
+/**\r
+ Get the size of the current device path instance.\r
+\r
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+ structure.\r
+ @param[out] InstanceSize The size of the current device path instance.\r
+ @param[out] EntireDevicePathEnd Indicate whether the instance is the last\r
+ one in the device path strucure.\r
+\r
+ @retval EFI_SUCCESS The size of the current device path instance is fetched.\r
+ @retval Others Fails to get the size of the current device path instance.\r
+\r
+**/\r
+EFI_STATUS\r
+GetDevicePathInstanceSize (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ OUT UINTN *InstanceSize,\r
+ OUT BOOLEAN *EntireDevicePathEnd\r
+ );\r
+\r
+/**\r
+ Check the validity of the device path of a ATA AHCI host controller.\r
+\r
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+ structure.\r
+ @param[in] DevicePathLength The length of the device path.\r
+\r
+ @retval EFI_SUCCESS The device path is valid.\r
+ @retval EFI_INVALID_PARAMETER The device path is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciIsHcDevicePathValid (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ IN UINTN DevicePathLength\r
+ );\r
+\r
+/**\r
+ Build the device path for an ATA device with given port and port multiplier number.\r
+\r
+ @param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+ data structure.\r
+ @param[in] Port The given port number.\r
+ @param[in] PortMultiplierPort The given port multiplier number.\r
+ @param[out] DevicePathLength The length of the device path in bytes specified\r
+ by DevicePath.\r
+ @param[out] DevicePath The device path of ATA device.\r
+\r
+ @retval EFI_SUCCESS The operation succeeds.\r
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciBuildDevicePath (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort,\r
+ OUT UINTN *DevicePathLength,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
+ );\r
+\r
+/**\r
+ Collect the ports that need to be enumerated on a controller for S3 phase.\r
+\r
+ @param[in] HcDevicePath Device path of the controller.\r
+ @param[in] HcDevicePathLength Length of the device path specified by\r
+ HcDevicePath.\r
+ @param[out] PortBitMap Bitmap that indicates the ports that need\r
+ to be enumerated on the controller.\r
+\r
+ @retval The number of ports that need to be enumerated.\r
+\r
+**/\r
+UINT8\r
+AhciS3GetEumeratePorts (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,\r
+ IN UINTN HcDevicePathLength,\r
+ OUT UINT32 *PortBitMap\r
+ );\r
+\r
+#endif\r
--- /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 of the BSD License\r
+# which accompanies this distribution. The 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
+[Defines]\r
+ INF_VERSION = 0x00010005\r
+ BASE_NAME = AhciPei\r
+ MODULE_UNI_FILE = AhciPei.uni\r
+ FILE_GUID = 79E5CA15-7A2D-4F37-A63B-D1C7BBCA47AD\r
+ MODULE_TYPE = PEIM\r
+ VERSION_STRING = 1.0\r
+ ENTRY_POINT = AtaAhciPeimEntry\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+# VALID_ARCHITECTURES = IA32 X64 EBC\r
+#\r
+\r
+[Sources]\r
+ AhciPei.c\r
+ AhciPei.h\r
+ AhciPeiPassThru.c\r
+ AhciPeiPassThru.h\r
+ AhciPeiS3.c\r
+ AhciPeiStorageSecurity.c\r
+ AhciPeiStorageSecurity.h\r
+ AhciMode.c\r
+ DevicePath.c\r
+ DmaMem.c\r
+\r
+[Packages]\r
+ MdePkg/MdePkg.dec\r
+ MdeModulePkg/MdeModulePkg.dec\r
+\r
+[LibraryClasses]\r
+ DebugLib\r
+ PeiServicesLib\r
+ MemoryAllocationLib\r
+ BaseMemoryLib\r
+ IoLib\r
+ TimerLib\r
+ LockBoxLib\r
+ PeimEntryPoint\r
+\r
+[Ppis]\r
+ gEdkiiPeiAtaAhciHostControllerPpiGuid ## CONSUMES\r
+ gEdkiiIoMmuPpiGuid ## CONSUMES\r
+ gEfiEndOfPeiSignalPpiGuid ## CONSUMES\r
+ gEdkiiPeiAtaPassThruPpiGuid ## SOMETIMES_PRODUCES\r
+ gEdkiiPeiStorageSecurityCommandPpiGuid ## SOMETIMES_PRODUCES\r
+\r
+[Guids]\r
+ gS3StorageDeviceInitListGuid ## SOMETIMES_CONSUMES ## UNDEFINED\r
+\r
+[Depex]\r
+ gEfiPeiMemoryDiscoveredPpiGuid AND\r
+ gEfiPeiMasterBootModePpiGuid AND\r
+ gEdkiiPeiAtaAhciHostControllerPpiGuid\r
+\r
+[UserExtensions.TianoCore."ExtraFiles"]\r
+ AhciPeiExtra.uni\r
--- /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
+\r
+#string STR_MODULE_ABSTRACT #language en-US "Manage AHCI mode ATA hard disk device at PEI phase"\r
+\r
+#string STR_MODULE_DESCRIPTION #language en-US "The AhciPei driver is used to manage ATA hard disk device working under AHCI mode at PEI phase."\r
--- /dev/null
+// /** @file\r
+// AhciPei Localized Strings and Content\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
+#string STR_PROPERTIES_MODULE_NAME\r
+#language en-US\r
+"AHCI Bus Peim"\r
--- /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
+/**\r
+ Traverse the attached ATA devices list to find out the device with given Port\r
+ and PortMultiplierPort.\r
+\r
+ @param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+ instance.\r
+ @param[in] Port The port number of the ATA device.\r
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA device.\r
+\r
+ @retval The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device\r
+ info to access.\r
+\r
+**/\r
+PEI_AHCI_ATA_DEVICE_DATA *\r
+SearchDeviceByPort (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort\r
+ )\r
+{\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ LIST_ENTRY *Node;\r
+\r
+ Node = GetFirstNode (&Private->DeviceList);\r
+ while (!IsNull (&Private->DeviceList, Node)) {\r
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+ if ((DeviceData->Port == Port) &&\r
+ (DeviceData->PortMultiplier == PortMultiplierPort)) {\r
+ return DeviceData;\r
+ }\r
+\r
+ Node = GetNextNode (&Private->DeviceList, Node);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Sends an ATA command to an ATA device that is attached to the ATA controller.\r
+\r
+ @param[in] Private Pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+ @param[in] Port The port number of the ATA device.\r
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA\r
+ device.\r
+ @param[in] FisIndex The index of the FIS.\r
+ @param[in,out] Packet A pointer to the ATA command to send to\r
+ the ATA device specified by Port and\r
+ PortMultiplierPort.\r
+\r
+ @retval EFI_SUCCESS The ATA command was sent by the host. For\r
+ bi-directional commands, InTransferLength bytes\r
+ were transferred from InDataBuffer. For write\r
+ and bi-directional commands, OutTransferLength\r
+ bytes were transferred by OutDataBuffer.\r
+ @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number\r
+ of bytes that could be transferred is returned\r
+ in InTransferLength. For write and bi-directional\r
+ commands, OutTransferLength bytes were transferred\r
+ by OutDataBuffer.\r
+ @retval EFI_NOT_READY The ATA command could not be sent because there\r
+ are too many ATA commands already queued. The\r
+ caller may retry again later.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to\r
+ send the ATA command.\r
+ @retval EFI_INVALID_PARAMETER Port, PortMultiplierPort, or the contents of\r
+ Acb are invalid. The ATA command was not sent,\r
+ so no additional status information is available.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciPassThruExecute (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort,\r
+ IN UINT8 FisIndex,\r
+ IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ switch (Packet->Protocol) {\r
+ case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:\r
+ Status = AhciNonDataTransfer (\r
+ Private,\r
+ (UINT8) Port,\r
+ (UINT8) PortMultiplierPort,\r
+ FisIndex,\r
+ Packet->Acb,\r
+ Packet->Asb,\r
+ Packet->Timeout\r
+ );\r
+ break;\r
+ case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:\r
+ Status = AhciPioTransfer (\r
+ Private,\r
+ (UINT8) Port,\r
+ (UINT8) PortMultiplierPort,\r
+ FisIndex,\r
+ TRUE,\r
+ Packet->Acb,\r
+ Packet->Asb,\r
+ Packet->InDataBuffer,\r
+ Packet->InTransferLength,\r
+ Packet->Timeout\r
+ );\r
+ break;\r
+ case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:\r
+ Status = AhciPioTransfer (\r
+ Private,\r
+ (UINT8) Port,\r
+ (UINT8) PortMultiplierPort,\r
+ FisIndex,\r
+ FALSE,\r
+ Packet->Acb,\r
+ Packet->Asb,\r
+ Packet->OutDataBuffer,\r
+ Packet->OutTransferLength,\r
+ Packet->Timeout\r
+ );\r
+ break;\r
+ default:\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Sends an ATA command to an ATA device that is attached to the ATA controller.\r
+\r
+ @param[in] This The PPI instance pointer.\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,out] Packet A pointer to the ATA command to send to\r
+ the ATA device specified by Port and\r
+ PortMultiplierPort.\r
+\r
+ @retval EFI_SUCCESS The ATA command was sent by the host. For\r
+ bi-directional commands, InTransferLength bytes\r
+ were transferred from InDataBuffer. For write\r
+ and bi-directional commands, OutTransferLength\r
+ bytes were transferred by OutDataBuffer.\r
+ @retval EFI_NOT_FOUND The specified ATA device is not found.\r
+ @retval EFI_INVALID_PARAMETER The contents of Acb are invalid. The ATA command\r
+ was not sent, so no additional status information\r
+ is available.\r
+ @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number\r
+ of bytes that could be transferred is returned\r
+ in InTransferLength. For write and bi-directional\r
+ commands, OutTransferLength bytes were transferred\r
+ by OutDataBuffer.\r
+ @retval EFI_NOT_READY The ATA command could not be sent because there\r
+ are too many ATA commands already queued. The\r
+ caller may retry again later.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to\r
+ send the ATA command.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruPassThru (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort,\r
+ IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet\r
+ )\r
+{\r
+ UINT32 IoAlign;\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ UINT32 MaxSectorCount;\r
+ UINT32 BlockSize;\r
+\r
+ if (This == NULL || Packet == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ IoAlign = This->Mode->IoAlign;\r
+ if ((IoAlign > 1) && !IS_ALIGNED (Packet->InDataBuffer, IoAlign)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((IoAlign > 1) && !IS_ALIGNED (Packet->OutDataBuffer, IoAlign)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((IoAlign > 1) && !IS_ALIGNED (Packet->Asb, IoAlign)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+ DeviceData = SearchDeviceByPort (Private, Port, PortMultiplierPort);\r
+ if (DeviceData == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
+ BlockSize = DeviceData->Media.BlockSize;\r
+\r
+ //\r
+ // Convert the transfer length from sector count to byte.\r
+ //\r
+ if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
+ (Packet->InTransferLength != 0)) {\r
+ Packet->InTransferLength = Packet->InTransferLength * BlockSize;\r
+ }\r
+\r
+ //\r
+ // Convert the transfer length from sector count to byte.\r
+ //\r
+ if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
+ (Packet->OutTransferLength != 0)) {\r
+ Packet->OutTransferLength = Packet->OutTransferLength * BlockSize;\r
+ }\r
+\r
+ //\r
+ // If the data buffer described by InDataBuffer/OutDataBuffer and\r
+ // InTransferLength/OutTransferLength is too big to be transferred in a single\r
+ // command, then no data is transferred and EFI_BAD_BUFFER_SIZE is returned.\r
+ //\r
+ if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * BlockSize)) ||\r
+ ((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * BlockSize))) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ return AhciPassThruExecute (\r
+ Private,\r
+ DeviceData->Port,\r
+ DeviceData->PortMultiplier,\r
+ DeviceData->FisIndex,\r
+ Packet\r
+ );\r
+}\r
+\r
+/**\r
+ Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.\r
+ These can either be the list of ports where ATA devices are actually present or the\r
+ list of legal port numbers for the ATA controller. Regardless, the caller of this\r
+ function must probe the port number returned to see if an ATA device is actually\r
+ present at that location on the ATA controller.\r
+\r
+ The GetNextPort() function retrieves the port number on an ATA controller. If on\r
+ input Port is 0xFFFF, then the port number of the first port on the ATA controller\r
+ is returned in Port and EFI_SUCCESS is returned.\r
+\r
+ If Port is a port number that was returned on a previous call to GetNextPort(),\r
+ then the port number of the next port on the ATA controller is returned in Port,\r
+ and EFI_SUCCESS is returned. If Port is not 0xFFFF and Port was not returned on\r
+ a previous call to GetNextPort(), then EFI_INVALID_PARAMETER is returned.\r
+\r
+ If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND\r
+ is returned.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in,out] Port On input, a pointer to the port number on the ATA controller.\r
+ On output, a pointer to the next port number on the ATA\r
+ controller. An input value of 0xFFFF retrieves the first\r
+ port number on the ATA controller.\r
+\r
+ @retval EFI_SUCCESS The next port number on the ATA controller was\r
+ returned in Port.\r
+ @retval EFI_NOT_FOUND There are no more ports on this ATA controller.\r
+ @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned\r
+ on a previous call to GetNextPort().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetNextPort (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ IN OUT UINT16 *Port\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ LIST_ENTRY *Node;\r
+\r
+ if (This == NULL || Port == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+\r
+ if (*Port == 0xFFFF) {\r
+ //\r
+ // If the Port is all 0xFF's, start to traverse the device list from the\r
+ // beginning.\r
+ //\r
+ Node = GetFirstNode (&Private->DeviceList);\r
+ if (!IsNull (&Private->DeviceList, Node)) {\r
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+ *Port = DeviceData->Port;\r
+ goto Exit;\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+ } else if (*Port == Private->PreviousPort) {\r
+ Node = GetFirstNode (&Private->DeviceList);\r
+\r
+ while (!IsNull (&Private->DeviceList, Node)) {\r
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+ if (DeviceData->Port > *Port){\r
+ *Port = DeviceData->Port;\r
+ goto Exit;\r
+ }\r
+\r
+ Node = GetNextNode (&Private->DeviceList, Node);\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+ } else {\r
+ //\r
+ // Port is not equal to all 0xFF's and not equal to previous return value.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+Exit:\r
+ //\r
+ // Update the PreviousPort.\r
+ //\r
+ Private->PreviousPort = *Port;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Used to retrieve the list of legal port multiplier port numbers for ATA devices\r
+ on a port of an ATA controller. These can either be the list of port multiplier\r
+ ports where ATA devices are actually present on port or the list of legal port\r
+ multiplier ports on that port. Regardless, the caller of this function must probe\r
+ the port number and port multiplier port number returned to see if an ATA device\r
+ is actually present.\r
+\r
+ The GetNextDevice() function retrieves the port multiplier port number of an ATA\r
+ device present on a port of an ATA controller.\r
+\r
+ If PortMultiplierPort points to a port multiplier port number value that was\r
+ returned on a previous call to GetNextDevice(), then the port multiplier port\r
+ number of the next ATA device on the port of the ATA controller is returned in\r
+ PortMultiplierPort, and EFI_SUCCESS is returned.\r
+\r
+ If PortMultiplierPort points to 0xFFFF, then the port multiplier port number\r
+ of the first ATA device on port of the ATA controller is returned in PortMultiplierPort\r
+ and EFI_SUCCESS is returned.\r
+\r
+ If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort\r
+ was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER\r
+ is returned.\r
+\r
+ If PortMultiplierPort is the port multiplier port number of the last ATA device\r
+ on the port of the ATA controller, then EFI_NOT_FOUND is returned.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] Port The port number present on the ATA controller.\r
+ @param[in,out] PortMultiplierPort On input, a pointer to the port multiplier\r
+ port number of an ATA device present on the\r
+ ATA controller. If on input a PortMultiplierPort\r
+ of 0xFFFF is specified, then the port multiplier\r
+ port number of the first ATA device is returned.\r
+ On output, a pointer to the port multiplier port\r
+ number of the next ATA device present on an ATA\r
+ controller.\r
+\r
+ @retval EFI_SUCCESS The port multiplier port number of the next ATA\r
+ device on the port of the ATA controller was\r
+ returned in PortMultiplierPort.\r
+ @retval EFI_NOT_FOUND There are no more ATA devices on this port of\r
+ the ATA controller.\r
+ @retval EFI_INVALID_PARAMETER PortMultiplierPort is not 0xFFFF, and PortMultiplierPort\r
+ was not returned on a previous call to GetNextDevice().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetNextDevice (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ IN UINT16 Port,\r
+ IN OUT UINT16 *PortMultiplierPort\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ LIST_ENTRY *Node;\r
+\r
+ if (This == NULL || PortMultiplierPort == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+\r
+ if (Private->PreviousPortMultiplier == 0xFFFF) {\r
+ //\r
+ // If a device is directly attached on a port, previous call to this\r
+ // function will return the value 0xFFFF for PortMultiplierPort. In\r
+ // this case, there should be no more device on the port multiplier.\r
+ //\r
+ Private->PreviousPortMultiplier = 0;\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (*PortMultiplierPort == Private->PreviousPortMultiplier) {\r
+ Node = GetFirstNode (&Private->DeviceList);\r
+\r
+ while (!IsNull (&Private->DeviceList, Node)) {\r
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+ if ((DeviceData->Port == Port) &&\r
+ (DeviceData->PortMultiplier > *PortMultiplierPort)){\r
+ *PortMultiplierPort = DeviceData->PortMultiplier;\r
+ goto Exit;\r
+ }\r
+\r
+ Node = GetNextNode (&Private->DeviceList, Node);\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+ } else if (*PortMultiplierPort == 0xFFFF) {\r
+ //\r
+ // If the PortMultiplierPort is all 0xFF's, start to traverse the device list\r
+ // from the beginning.\r
+ //\r
+ Node = GetFirstNode (&Private->DeviceList);\r
+\r
+ while (!IsNull (&Private->DeviceList, Node)) {\r
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+ if (DeviceData->Port == Port){\r
+ *PortMultiplierPort = DeviceData->PortMultiplier;\r
+ goto Exit;\r
+ }\r
+\r
+ Node = GetNextNode (&Private->DeviceList, Node);\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+ } else {\r
+ //\r
+ // PortMultiplierPort is not equal to all 0xFF's and not equal to previous\r
+ // return value.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+Exit:\r
+ //\r
+ // Update the PreviousPortMultiplier.\r
+ //\r
+ Private->PreviousPortMultiplier = *PortMultiplierPort;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Gets the device path information of the underlying ATA host controller.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[out] DevicePathLength The length of the device path in bytes specified\r
+ by DevicePath.\r
+ @param[out] DevicePath The device path of the underlying ATA host controller.\r
+ This field re-uses EFI Device Path Protocol as\r
+ defined by Section 10.2 EFI Device Path Protocol\r
+ of UEFI 2.7 Specification.\r
+\r
+ @retval EFI_SUCCESS The device path of the ATA host controller has\r
+ been successfully returned.\r
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to return the device path.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetDevicePath (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ OUT UINTN *DevicePathLength,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+\r
+ if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+\r
+ *DevicePathLength = Private->DevicePathLength;\r
+ *DevicePath = AllocateCopyPool (Private->DevicePathLength, Private->DevicePath);\r
+ if (*DevicePath == NULL) {\r
+ *DevicePathLength = 0;\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /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
+#ifndef _AHCI_PEI_PASSTHRU_H_\r
+#define _AHCI_PEI_PASSTHRU_H_\r
+\r
+/**\r
+ Sends an ATA command to an ATA device that is attached to the ATA controller.\r
+\r
+ @param[in] This The PPI instance pointer.\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,out] Packet A pointer to the ATA command to send to\r
+ the ATA device specified by Port and\r
+ PortMultiplierPort.\r
+\r
+ @retval EFI_SUCCESS The ATA command was sent by the host. For\r
+ bi-directional commands, InTransferLength bytes\r
+ were transferred from InDataBuffer. For write\r
+ and bi-directional commands, OutTransferLength\r
+ bytes were transferred by OutDataBuffer.\r
+ @retval EFI_NOT_FOUND The specified ATA device is not found.\r
+ @retval EFI_INVALID_PARAMETER The contents of Acb are invalid. The ATA command\r
+ was not sent, so no additional status information\r
+ is available.\r
+ @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number\r
+ of bytes that could be transferred is returned\r
+ in InTransferLength. For write and bi-directional\r
+ commands, OutTransferLength bytes were transferred\r
+ by OutDataBuffer.\r
+ @retval EFI_NOT_READY The ATA command could not be sent because there\r
+ are too many ATA commands already queued. The\r
+ caller may retry again later.\r
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to\r
+ send the ATA command.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruPassThru (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort,\r
+ IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet\r
+ );\r
+\r
+/**\r
+ Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.\r
+ These can either be the list of ports where ATA devices are actually present or the\r
+ list of legal port numbers for the ATA controller. Regardless, the caller of this\r
+ function must probe the port number returned to see if an ATA device is actually\r
+ present at that location on the ATA controller.\r
+\r
+ The GetNextPort() function retrieves the port number on an ATA controller. If on\r
+ input Port is 0xFFFF, then the port number of the first port on the ATA controller\r
+ is returned in Port and EFI_SUCCESS is returned.\r
+\r
+ If Port is a port number that was returned on a previous call to GetNextPort(),\r
+ then the port number of the next port on the ATA controller is returned in Port,\r
+ and EFI_SUCCESS is returned. If Port is not 0xFFFF and Port was not returned on\r
+ a previous call to GetNextPort(), then EFI_INVALID_PARAMETER is returned.\r
+\r
+ If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND\r
+ is returned.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in,out] Port On input, a pointer to the port number on the ATA controller.\r
+ On output, a pointer to the next port number on the ATA\r
+ controller. An input value of 0xFFFF retrieves the first\r
+ port number on the ATA controller.\r
+\r
+ @retval EFI_SUCCESS The next port number on the ATA controller was\r
+ returned in Port.\r
+ @retval EFI_NOT_FOUND There are no more ports on this ATA controller.\r
+ @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned\r
+ on a previous call to GetNextPort().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetNextPort (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ IN OUT UINT16 *Port\r
+ );\r
+\r
+/**\r
+ Used to retrieve the list of legal port multiplier port numbers for ATA devices\r
+ on a port of an ATA controller. These can either be the list of port multiplier\r
+ ports where ATA devices are actually present on port or the list of legal port\r
+ multiplier ports on that port. Regardless, the caller of this function must probe\r
+ the port number and port multiplier port number returned to see if an ATA device\r
+ is actually present.\r
+\r
+ The GetNextDevice() function retrieves the port multiplier port number of an ATA\r
+ device present on a port of an ATA controller.\r
+\r
+ If PortMultiplierPort points to a port multiplier port number value that was\r
+ returned on a previous call to GetNextDevice(), then the port multiplier port\r
+ number of the next ATA device on the port of the ATA controller is returned in\r
+ PortMultiplierPort, and EFI_SUCCESS is returned.\r
+\r
+ If PortMultiplierPort points to 0xFFFF, then the port multiplier port number\r
+ of the first ATA device on port of the ATA controller is returned in PortMultiplierPort\r
+ and EFI_SUCCESS is returned.\r
+\r
+ If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort\r
+ was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER\r
+ is returned.\r
+\r
+ If PortMultiplierPort is the port multiplier port number of the last ATA device\r
+ on the port of the ATA controller, then EFI_NOT_FOUND is returned.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] Port The port number present on the ATA controller.\r
+ @param[in,out] PortMultiplierPort On input, a pointer to the port multiplier\r
+ port number of an ATA device present on the\r
+ ATA controller. If on input a PortMultiplierPort\r
+ of 0xFFFF is specified, then the port multiplier\r
+ port number of the first ATA device is returned.\r
+ On output, a pointer to the port multiplier port\r
+ number of the next ATA device present on an ATA\r
+ controller.\r
+\r
+ @retval EFI_SUCCESS The port multiplier port number of the next ATA\r
+ device on the port of the ATA controller was\r
+ returned in PortMultiplierPort.\r
+ @retval EFI_NOT_FOUND There are no more ATA devices on this port of\r
+ the ATA controller.\r
+ @retval EFI_INVALID_PARAMETER PortMultiplierPort is not 0xFFFF, and PortMultiplierPort\r
+ was not returned on a previous call to GetNextDevice().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetNextDevice (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ IN UINT16 Port,\r
+ IN OUT UINT16 *PortMultiplierPort\r
+ );\r
+\r
+/**\r
+ Gets the device path information of the underlying ATA host controller.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[out] DevicePathLength The length of the device path in bytes specified\r
+ by DevicePath.\r
+ @param[out] DevicePath The device path of the underlying ATA host controller.\r
+ This field re-uses EFI Device Path Protocol as\r
+ defined by Section 10.2 EFI Device Path Protocol\r
+ of UEFI 2.7 Specification.\r
+\r
+ @retval EFI_SUCCESS The device path of the ATA host controller has\r
+ been successfully returned.\r
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to return the device path.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetDevicePath (\r
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,\r
+ OUT UINTN *DevicePathLength,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
+ );\r
+\r
+#endif\r
--- /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
+#include <Guid/S3StorageDeviceInitList.h>\r
+\r
+#include <Library/LockBoxLib.h>\r
+\r
+/**\r
+ Collect the ports that need to be enumerated on a controller for S3 phase.\r
+\r
+ @param[in] HcDevicePath Device path of the controller.\r
+ @param[in] HcDevicePathLength Length of the device path specified by\r
+ HcDevicePath.\r
+ @param[out] PortBitMap Bitmap that indicates the ports that need\r
+ to be enumerated on the controller.\r
+\r
+ @retval The number of ports that need to be enumerated.\r
+\r
+**/\r
+UINT8\r
+AhciS3GetEumeratePorts (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *HcDevicePath,\r
+ IN UINTN HcDevicePathLength,\r
+ OUT UINT32 *PortBitMap\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 DummyData;\r
+ UINTN S3InitDevicesLength;\r
+ EFI_DEVICE_PATH_PROTOCOL *S3InitDevices;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;\r
+ UINTN DevicePathInstLength;\r
+ BOOLEAN EntireEnd;\r
+ SATA_DEVICE_PATH *SataDeviceNode;\r
+\r
+ *PortBitMap = 0;\r
+\r
+ //\r
+ // From the LockBox, get the list of device paths for devices need to be\r
+ // initialized in S3.\r
+ //\r
+ S3InitDevices = NULL;\r
+ S3InitDevicesLength = sizeof (DummyData);\r
+ EntireEnd = FALSE;\r
+ Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, &DummyData, &S3InitDevicesLength);\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return 0;\r
+ } else {\r
+ S3InitDevices = AllocatePool (S3InitDevicesLength);\r
+ if (S3InitDevices == NULL) {\r
+ return 0;\r
+ }\r
+\r
+ Status = RestoreLockBox (&gS3StorageDeviceInitListGuid, S3InitDevices, &S3InitDevicesLength);\r
+ if (EFI_ERROR (Status)) {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ if (S3InitDevices == NULL) {\r
+ return 0;\r
+ }\r
+\r
+ //\r
+ // Only enumerate the ports that exist in the device list.\r
+ //\r
+ do {\r
+ //\r
+ // Fetch the size of current device path instance.\r
+ //\r
+ Status = GetDevicePathInstanceSize (\r
+ S3InitDevices,\r
+ &DevicePathInstLength,\r
+ &EntireEnd\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ DevicePathInst = S3InitDevices;\r
+ S3InitDevices = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN) S3InitDevices + DevicePathInstLength);\r
+\r
+ if (HcDevicePathLength >= DevicePathInstLength) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Compare the device paths to determine if the device is managed by this\r
+ // controller.\r
+ //\r
+ if (CompareMem (\r
+ DevicePathInst,\r
+ HcDevicePath,\r
+ HcDevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)\r
+ ) == 0) {\r
+ //\r
+ // Get the port number.\r
+ //\r
+ while (DevicePathInst->Type != END_DEVICE_PATH_TYPE) {\r
+ if ((DevicePathInst->Type == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathInst->SubType == MSG_SATA_DP)) {\r
+ SataDeviceNode = (SATA_DEVICE_PATH *) DevicePathInst;\r
+ //\r
+ // For now, the driver only support upto AHCI_MAX_PORTS ports and\r
+ // devices directly connected to a HBA.\r
+ //\r
+ if ((SataDeviceNode->HBAPortNumber >= AHCI_MAX_PORTS) ||\r
+ (SataDeviceNode->PortMultiplierPortNumber != 0xFFFF)) {\r
+ break;\r
+ }\r
+ *PortBitMap |= (UINT32)BIT0 << SataDeviceNode->HBAPortNumber;\r
+ break;\r
+ }\r
+ DevicePathInst = NextDevicePathNode (DevicePathInst);\r
+ }\r
+ }\r
+ } while (!EntireEnd);\r
+\r
+ //\r
+ // Return the number of ports need to be enumerated on this controller.\r
+ //\r
+ return AhciGetNumberOfPortsFromMap (*PortBitMap);\r
+}\r
--- /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
+/**\r
+ Traverse the attached ATA devices list to find out the device with given trust\r
+ computing device index.\r
+\r
+ @param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+ instance.\r
+ @param[in] TrustComputingDeviceIndex The trust computing device index.\r
+\r
+ @retval The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device\r
+ info to access.\r
+\r
+**/\r
+PEI_AHCI_ATA_DEVICE_DATA *\r
+SearchTrustComputingDeviceByIndex (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINTN TrustComputingDeviceIndex\r
+ )\r
+{\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ LIST_ENTRY *Node;\r
+\r
+ Node = GetFirstNode (&Private->DeviceList);\r
+ while (!IsNull (&Private->DeviceList, Node)) {\r
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+ if (DeviceData->TrustComputingDeviceIndex == TrustComputingDeviceIndex) {\r
+ return DeviceData;\r
+ }\r
+\r
+ Node = GetNextNode (&Private->DeviceList, Node);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Gets the count of storage security devices that one specific driver detects.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[out] NumberofDevices The number of storage security devices discovered.\r
+\r
+ @retval EFI_SUCCESS The operation performed successfully.\r
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecurityGetDeviceNo (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ OUT UINTN *NumberofDevices\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+\r
+ if (This == NULL || NumberofDevices == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);\r
+ *NumberofDevices = Private->TrustComputingDevices;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Gets the device path of a specific storage security device.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] DeviceIndex Specifies the storage security device to which\r
+ the function wants to talk. Because the driver\r
+ that implements Storage Security Command PPIs\r
+ will manage multiple storage devices, the PPIs\r
+ that want to talk to a single device must specify\r
+ the device index that was assigned during the\r
+ enumeration process. This index is a number from\r
+ one to NumberofDevices.\r
+ @param[out] DevicePathLength The length of the device path in bytes specified\r
+ by DevicePath.\r
+ @param[out] DevicePath The device path of storage security device.\r
+ This field re-uses EFI Device Path Protocol as\r
+ defined by Section 10.2 EFI Device Path Protocol\r
+ of UEFI 2.7 Specification.\r
+\r
+ @retval EFI_SUCCESS The operation succeeds.\r
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.\r
+ @retval EFI_NOT_FOUND The specified storage security device not found.\r
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecurityGetDevicePath (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ OUT UINTN *DevicePathLength,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+ EFI_STATUS Status;\r
+\r
+ if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);\r
+ if ((DeviceIndex == 0) || (DeviceIndex > Private->TrustComputingDevices)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DeviceData = SearchTrustComputingDeviceByIndex (Private, DeviceIndex);\r
+ if (DeviceData == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = AhciBuildDevicePath (\r
+ Private,\r
+ DeviceData->Port,\r
+ DeviceData->PortMultiplier,\r
+ DevicePathLength,\r
+ DevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Send a security protocol command to a device that receives data and/or the result\r
+ of one or more commands sent by SendData.\r
+\r
+ The ReceiveData function sends a security protocol command to the given DeviceIndex.\r
+ The security protocol command sent is defined by SecurityProtocolId and contains\r
+ the security protocol specific data SecurityProtocolSpecificData. The function\r
+ returns the data from the security protocol command in PayloadBuffer.\r
+\r
+ For devices supporting the SCSI command set, the security protocol command is sent\r
+ using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+ For devices supporting the ATA command set, the security protocol command is sent\r
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize\r
+ is non-zero.\r
+\r
+ If the PayloadBufferSize is zero, the security protocol command is sent using the\r
+ Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+ If PayloadBufferSize is too small to store the available data from the security\r
+ protocol command, the function shall copy PayloadBufferSize bytes into the\r
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+ the function shall return EFI_INVALID_PARAMETER.\r
+\r
+ If the given DeviceIndex does not support security protocol commands, the function\r
+ shall return EFI_UNSUPPORTED.\r
+\r
+ If the security protocol fails to complete within the Timeout period, the function\r
+ shall return EFI_TIMEOUT.\r
+\r
+ If the security protocol command completes without an error, the function shall\r
+ return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+ function shall return EFI_DEVICE_ERROR.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] DeviceIndex Specifies the storage security device to which the\r
+ function wants to talk. Because the driver that\r
+ implements Storage Security Command PPIs will manage\r
+ multiple storage devices, the PPIs that want to talk\r
+ to a single device must specify the device index\r
+ that was assigned during the enumeration process.\r
+ This index is a number from one to NumberofDevices.\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[in] SecurityProtocolId\r
+ The value of the "Security Protocol" parameter of\r
+ 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 be\r
+ sent.\r
+ @param[in] PayloadBufferSize\r
+ Size in bytes of the payload data buffer.\r
+ @param[out] PayloadBuffer A pointer to a destination buffer to store the\r
+ security protocol command specific payload data\r
+ for the security protocol command. The caller is\r
+ responsible for having either implicit or explicit\r
+ ownership of the buffer.\r
+ @param[out] PayloadTransferSize\r
+ A pointer to a buffer to store the size in bytes\r
+ of the data written to the payload data buffer.\r
+\r
+ @retval EFI_SUCCESS The security protocol command completed\r
+ successfully.\r
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to\r
+ store the available data from the device.\r
+ The PayloadBuffer contains the truncated\r
+ data.\r
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support\r
+ security protocol commands.\r
+ @retval EFI_DEVICE_ERROR The security protocol command completed\r
+ with an error.\r
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize\r
+ is NULL and PayloadBufferSize is non-zero.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the\r
+ security protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecurityReceiveData (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN PayloadBufferSize,\r
+ OUT VOID *PayloadBuffer,\r
+ OUT UINTN *PayloadTransferSize\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+\r
+ if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL) || (PayloadBufferSize == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);\r
+ if ((DeviceIndex == 0) || (DeviceIndex > Private->TrustComputingDevices)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DeviceData = SearchTrustComputingDeviceByIndex (Private, DeviceIndex);\r
+ if (DeviceData == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ ASSERT ((DeviceData->IdentifyData->trusted_computing_support & BIT0) != 0);\r
+ if ((DeviceData->IdentifyData->trusted_computing_support & BIT0) == 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return TrustTransferAtaDevice (\r
+ DeviceData,\r
+ PayloadBuffer,\r
+ SecurityProtocolId,\r
+ SecurityProtocolSpecificData,\r
+ PayloadBufferSize,\r
+ FALSE,\r
+ Timeout,\r
+ PayloadTransferSize\r
+ );\r
+}\r
+\r
+/**\r
+ Send a security protocol command to a device.\r
+\r
+ The SendData function sends a security protocol command containing the payload\r
+ PayloadBuffer to the given DeviceIndex. The security protocol command sent is\r
+ defined by SecurityProtocolId and contains the security protocol specific data\r
+ SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+ specific padding for the command payload, the SendData function shall add padding\r
+ bytes to the command payload to satisfy the padding requirements.\r
+\r
+ For devices supporting the SCSI command set, the security protocol command is\r
+ sent using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+ For devices supporting the ATA command set, the security protocol command is\r
+ sent using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize\r
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command\r
+ is sent using the Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+ return EFI_INVALID_PARAMETER.\r
+\r
+ If the given DeviceIndex does not support security protocol commands, the function\r
+ shall return EFI_UNSUPPORTED.\r
+\r
+ If the security protocol fails to complete within the Timeout period, the function\r
+ shall return EFI_TIMEOUT.\r
+\r
+ If the security protocol command completes without an error, the function shall\r
+ return EFI_SUCCESS. If the security protocol command completes with an error,\r
+ the functio shall return EFI_DEVICE_ERROR.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] DeviceIndex The ID of the device.\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[in] SecurityProtocolId\r
+ The value of the "Security Protocol" parameter of\r
+ 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 be\r
+ sent.\r
+ @param[in] PayloadBufferSize Size in bytes of the payload data buffer.\r
+ @param[in] PayloadBuffer A pointer to a destination buffer to store the\r
+ security protocol command specific payload data\r
+ for the security protocol command.\r
+\r
+ @retval EFI_SUCCESS The security protocol command completed successfully.\r
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support security\r
+ protocol commands.\r
+ @retval EFI_DEVICE_ERROR The security protocol command completed with\r
+ an error.\r
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize\r
+ is non-zero.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security\r
+ protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecuritySendData (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN PayloadBufferSize,\r
+ IN VOID *PayloadBuffer\r
+ )\r
+{\r
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
+\r
+ if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_STROAGE_SECURITY (This);\r
+ if ((DeviceIndex == 0) || (DeviceIndex > Private->TrustComputingDevices)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DeviceData = SearchTrustComputingDeviceByIndex (Private, DeviceIndex);\r
+ if (DeviceData == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ ASSERT ((DeviceData->IdentifyData->trusted_computing_support & BIT0) != 0);\r
+ if ((DeviceData->IdentifyData->trusted_computing_support & BIT0) == 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ return TrustTransferAtaDevice (\r
+ DeviceData,\r
+ PayloadBuffer,\r
+ SecurityProtocolId,\r
+ SecurityProtocolSpecificData,\r
+ PayloadBufferSize,\r
+ TRUE,\r
+ Timeout,\r
+ NULL\r
+ );\r
+}\r
--- /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
+#ifndef _AHCI_PEI_STORAGE_SECURITY_H_\r
+#define _AHCI_PEI_STORAGE_SECURITY_H_\r
+\r
+/**\r
+ Gets the count of storage security devices that one specific driver detects.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[out] NumberofDevices The number of storage security devices discovered.\r
+\r
+ @retval EFI_SUCCESS The operation performed successfully.\r
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecurityGetDeviceNo (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ OUT UINTN *NumberofDevices\r
+ );\r
+\r
+/**\r
+ Gets the device path of a specific storage security device.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] DeviceIndex Specifies the storage security device to which\r
+ the function wants to talk. Because the driver\r
+ that implements Storage Security Command PPIs\r
+ will manage multiple storage devices, the PPIs\r
+ that want to talk to a single device must specify\r
+ the device index that was assigned during the\r
+ enumeration process. This index is a number from\r
+ one to NumberofDevices.\r
+ @param[out] DevicePathLength The length of the device path in bytes specified\r
+ by DevicePath.\r
+ @param[out] DevicePath The device path of storage security device.\r
+ This field re-uses EFI Device Path Protocol as\r
+ defined by Section 10.2 EFI Device Path Protocol\r
+ of UEFI 2.7 Specification.\r
+\r
+ @retval EFI_SUCCESS The operation succeeds.\r
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.\r
+ @retval EFI_NOT_FOUND The specified storage security device not found.\r
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecurityGetDevicePath (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ OUT UINTN *DevicePathLength,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
+ );\r
+\r
+/**\r
+ Send a security protocol command to a device that receives data and/or the result\r
+ of one or more commands sent by SendData.\r
+\r
+ The ReceiveData function sends a security protocol command to the given DeviceIndex.\r
+ The security protocol command sent is defined by SecurityProtocolId and contains\r
+ the security protocol specific data SecurityProtocolSpecificData. The function\r
+ returns the data from the security protocol command in PayloadBuffer.\r
+\r
+ For devices supporting the SCSI command set, the security protocol command is sent\r
+ using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+ For devices supporting the ATA command set, the security protocol command is sent\r
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize\r
+ is non-zero.\r
+\r
+ If the PayloadBufferSize is zero, the security protocol command is sent using the\r
+ Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+ If PayloadBufferSize is too small to store the available data from the security\r
+ protocol command, the function shall copy PayloadBufferSize bytes into the\r
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+ the function shall return EFI_INVALID_PARAMETER.\r
+\r
+ If the given DeviceIndex does not support security protocol commands, the function\r
+ shall return EFI_UNSUPPORTED.\r
+\r
+ If the security protocol fails to complete within the Timeout period, the function\r
+ shall return EFI_TIMEOUT.\r
+\r
+ If the security protocol command completes without an error, the function shall\r
+ return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+ function shall return EFI_DEVICE_ERROR.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] DeviceIndex Specifies the storage security device to which the\r
+ function wants to talk. Because the driver that\r
+ implements Storage Security Command PPIs will manage\r
+ multiple storage devices, the PPIs that want to talk\r
+ to a single device must specify the device index\r
+ that was assigned during the enumeration process.\r
+ This index is a number from one to NumberofDevices.\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[in] SecurityProtocolId\r
+ The value of the "Security Protocol" parameter of\r
+ 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 be\r
+ sent.\r
+ @param[in] PayloadBufferSize\r
+ Size in bytes of the payload data buffer.\r
+ @param[out] PayloadBuffer A pointer to a destination buffer to store the\r
+ security protocol command specific payload data\r
+ for the security protocol command. The caller is\r
+ responsible for having either implicit or explicit\r
+ ownership of the buffer.\r
+ @param[out] PayloadTransferSize\r
+ A pointer to a buffer to store the size in bytes\r
+ of the data written to the payload data buffer.\r
+\r
+ @retval EFI_SUCCESS The security protocol command completed\r
+ successfully.\r
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to\r
+ store the available data from the device.\r
+ The PayloadBuffer contains the truncated\r
+ data.\r
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support\r
+ security protocol commands.\r
+ @retval EFI_DEVICE_ERROR The security protocol command completed\r
+ with an error.\r
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize\r
+ is NULL and PayloadBufferSize is non-zero.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the\r
+ security protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecurityReceiveData (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN PayloadBufferSize,\r
+ OUT VOID *PayloadBuffer,\r
+ OUT UINTN *PayloadTransferSize\r
+ );\r
+\r
+/**\r
+ Send a security protocol command to a device.\r
+\r
+ The SendData function sends a security protocol command containing the payload\r
+ PayloadBuffer to the given DeviceIndex. The security protocol command sent is\r
+ defined by SecurityProtocolId and contains the security protocol specific data\r
+ SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+ specific padding for the command payload, the SendData function shall add padding\r
+ bytes to the command payload to satisfy the padding requirements.\r
+\r
+ For devices supporting the SCSI command set, the security protocol command is\r
+ sent using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+ For devices supporting the ATA command set, the security protocol command is\r
+ sent using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize\r
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command\r
+ is sent using the Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+ return EFI_INVALID_PARAMETER.\r
+\r
+ If the given DeviceIndex does not support security protocol commands, the function\r
+ shall return EFI_UNSUPPORTED.\r
+\r
+ If the security protocol fails to complete within the Timeout period, the function\r
+ shall return EFI_TIMEOUT.\r
+\r
+ If the security protocol command completes without an error, the function shall\r
+ return EFI_SUCCESS. If the security protocol command completes with an error,\r
+ the functio shall return EFI_DEVICE_ERROR.\r
+\r
+ @param[in] This The PPI instance pointer.\r
+ @param[in] DeviceIndex The ID of the device.\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[in] SecurityProtocolId\r
+ The value of the "Security Protocol" parameter of\r
+ 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 be\r
+ sent.\r
+ @param[in] PayloadBufferSize Size in bytes of the payload data buffer.\r
+ @param[in] PayloadBuffer A pointer to a destination buffer to store the\r
+ security protocol command specific payload data\r
+ for the security protocol command.\r
+\r
+ @retval EFI_SUCCESS The security protocol command completed successfully.\r
+ @retval EFI_UNSUPPORTED The given DeviceIndex does not support security\r
+ protocol commands.\r
+ @retval EFI_DEVICE_ERROR The security protocol command completed with\r
+ an error.\r
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize\r
+ is non-zero.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security\r
+ protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciStorageSecuritySendData (\r
+ IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN PayloadBufferSize,\r
+ IN VOID *PayloadBuffer\r
+ );\r
+\r
+#endif\r
--- /dev/null
+/** @file\r
+ The device path help function.\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
+//\r
+// Template for a SATA Device Path node\r
+//\r
+SATA_DEVICE_PATH mAhciSataDevicePathNodeTemplate = {\r
+ { // Header\r
+ MESSAGING_DEVICE_PATH,\r
+ MSG_SATA_DP,\r
+ {\r
+ (UINT8) (sizeof (SATA_DEVICE_PATH)),\r
+ (UINT8) ((sizeof (SATA_DEVICE_PATH)) >> 8)\r
+ }\r
+ },\r
+ 0x0, // HBAPortNumber\r
+ 0xFFFF, // PortMultiplierPortNumber\r
+ 0x0 // Lun\r
+};\r
+\r
+//\r
+// Template for an End of entire Device Path node\r
+//\r
+EFI_DEVICE_PATH_PROTOCOL mAhciEndDevicePathNodeTemplate = {\r
+ END_DEVICE_PATH_TYPE,\r
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ {\r
+ (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),\r
+ (UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)\r
+ }\r
+};\r
+\r
+/**\r
+ Returns the 16-bit Length field of a device path node.\r
+\r
+ Returns the 16-bit Length field of the device path node specified by Node.\r
+ Node is not required to be aligned on a 16-bit boundary, so it is recommended\r
+ that a function such as ReadUnaligned16() be used to extract the contents of\r
+ the Length field.\r
+\r
+ If Node is NULL, then ASSERT().\r
+\r
+ @param Node A pointer to a device path node data structure.\r
+\r
+ @return The 16-bit Length field of the device path node specified by Node.\r
+\r
+**/\r
+UINTN\r
+DevicePathNodeLength (\r
+ IN CONST VOID *Node\r
+ )\r
+{\r
+ ASSERT (Node != NULL);\r
+ return ReadUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0]);\r
+}\r
+\r
+/**\r
+ Returns a pointer to the next node in a device path.\r
+\r
+ If Node is NULL, then ASSERT().\r
+\r
+ @param Node A pointer to a device path node data structure.\r
+\r
+ @return a pointer to the device path node that follows the device path node\r
+ specified by Node.\r
+\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+NextDevicePathNode (\r
+ IN CONST VOID *Node\r
+ )\r
+{\r
+ ASSERT (Node != NULL);\r
+ return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) + DevicePathNodeLength(Node));\r
+}\r
+\r
+/**\r
+ Get the size of the current device path instance.\r
+\r
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+ structure.\r
+ @param[out] InstanceSize The size of the current device path instance.\r
+ @param[out] EntireDevicePathEnd Indicate whether the instance is the last\r
+ one in the device path strucure.\r
+\r
+ @retval EFI_SUCCESS The size of the current device path instance is fetched.\r
+ @retval Others Fails to get the size of the current device path instance.\r
+\r
+**/\r
+EFI_STATUS\r
+GetDevicePathInstanceSize (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ OUT UINTN *InstanceSize,\r
+ OUT BOOLEAN *EntireDevicePathEnd\r
+ )\r
+{\r
+ EFI_DEVICE_PATH_PROTOCOL *Walker;\r
+\r
+ if (DevicePath == NULL || InstanceSize == NULL || EntireDevicePathEnd == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Find the end of the device path instance\r
+ //\r
+ Walker = DevicePath;\r
+ while (Walker->Type != END_DEVICE_PATH_TYPE) {\r
+ Walker = NextDevicePathNode (Walker);\r
+ }\r
+\r
+ //\r
+ // Check if 'Walker' points to the end of an entire device path\r
+ //\r
+ if (Walker->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {\r
+ *EntireDevicePathEnd = TRUE;\r
+ } else if (Walker->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {\r
+ *EntireDevicePathEnd = FALSE;\r
+ } else {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Compute the size of the device path instance\r
+ //\r
+ *InstanceSize = ((UINTN) Walker - (UINTN) (DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Check the validity of the device path of a ATA AHCI host controller.\r
+\r
+ @param[in] DevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+ structure.\r
+ @param[in] DevicePathLength The length of the device path.\r
+\r
+ @retval EFI_SUCCESS The device path is valid.\r
+ @retval EFI_INVALID_PARAMETER The device path is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciIsHcDevicePathValid (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ IN UINTN DevicePathLength\r
+ )\r
+{\r
+ EFI_DEVICE_PATH_PROTOCOL *Start;\r
+ UINTN Size;\r
+\r
+ if (DevicePath == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Validate the DevicePathLength is big enough to touch the first node.\r
+ //\r
+ if (DevicePathLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Start = DevicePath;\r
+ while (!(DevicePath->Type == END_DEVICE_PATH_TYPE &&\r
+ DevicePath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {\r
+ DevicePath = NextDevicePathNode (DevicePath);\r
+\r
+ //\r
+ // Prevent overflow and invalid zero in the 'Length' field of a device path\r
+ // node.\r
+ //\r
+ if ((UINTN) DevicePath <= (UINTN) Start) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Prevent touching memory beyond given DevicePathLength.\r
+ //\r
+ if ((UINTN) DevicePath - (UINTN) Start >\r
+ DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check if the device path and its size match each other.\r
+ //\r
+ Size = ((UINTN) DevicePath - (UINTN) Start) + sizeof (EFI_DEVICE_PATH_PROTOCOL);\r
+ if (Size != DevicePathLength) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Build the device path for an ATA device with given port and port multiplier number.\r
+\r
+ @param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+ data structure.\r
+ @param[in] Port The given port number.\r
+ @param[in] PortMultiplierPort The given port multiplier number.\r
+ @param[out] DevicePathLength The length of the device path in bytes specified\r
+ by DevicePath.\r
+ @param[out] DevicePath The device path of ATA device.\r
+\r
+ @retval EFI_SUCCESS The operation succeeds.\r
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciBuildDevicePath (\r
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
+ IN UINT16 Port,\r
+ IN UINT16 PortMultiplierPort,\r
+ OUT UINTN *DevicePathLength,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
+ )\r
+{\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathWalker;\r
+ SATA_DEVICE_PATH *SataDeviceNode;\r
+\r
+ if (DevicePathLength == NULL || DevicePath == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *DevicePathLength = Private->DevicePathLength + sizeof (SATA_DEVICE_PATH);\r
+ *DevicePath = AllocatePool (*DevicePathLength);\r
+ if (*DevicePath == NULL) {\r
+ *DevicePathLength = 0;\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Construct the host controller part device nodes\r
+ //\r
+ DevicePathWalker = *DevicePath;\r
+ CopyMem (\r
+ DevicePathWalker,\r
+ Private->DevicePath,\r
+ Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)\r
+ );\r
+\r
+ //\r
+ // Construct the SATA device node\r
+ //\r
+ DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +\r
+ (Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)));\r
+ CopyMem (\r
+ DevicePathWalker,\r
+ &mAhciSataDevicePathNodeTemplate,\r
+ sizeof (mAhciSataDevicePathNodeTemplate)\r
+ );\r
+ SataDeviceNode = (SATA_DEVICE_PATH *)DevicePathWalker;\r
+ SataDeviceNode->HBAPortNumber = Port;\r
+ SataDeviceNode->PortMultiplierPortNumber = PortMultiplierPort;\r
+\r
+ //\r
+ // Construct the end device node\r
+ //\r
+ DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +\r
+ sizeof (SATA_DEVICE_PATH));\r
+ CopyMem (\r
+ DevicePathWalker,\r
+ &mAhciEndDevicePathNodeTemplate,\r
+ sizeof (mAhciEndDevicePathNodeTemplate)\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+ The DMA memory help function.\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
+/**\r
+ Get IOMMU PPI.\r
+\r
+ @return Pointer to IOMMU PPI.\r
+\r
+**/\r
+EDKII_IOMMU_PPI *\r
+GetIoMmu (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EDKII_IOMMU_PPI *IoMmu;\r
+\r
+ IoMmu = NULL;\r
+ Status = PeiServicesLocatePpi (\r
+ &gEdkiiIoMmuPpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **) &IoMmu\r
+ );\r
+ if (!EFI_ERROR (Status) && (IoMmu != NULL)) {\r
+ return IoMmu;\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Provides the controller-specific addresses required to access system memory from a\r
+ DMA bus master.\r
+\r
+ @param Operation Indicates if the bus master is going to read or write to system memory.\r
+ @param HostAddress The system memory address to map to the PCI controller.\r
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
+ that were mapped.\r
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
+ access the hosts HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuMap (\r
+ IN EDKII_IOMMU_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 Attribute;\r
+ EDKII_IOMMU_PPI *IoMmu;\r
+\r
+ IoMmu = GetIoMmu ();\r
+\r
+ if (IoMmu != NULL) {\r
+ Status = IoMmu->Map (\r
+ IoMmu,\r
+ Operation,\r
+ HostAddress,\r
+ NumberOfBytes,\r
+ DeviceAddress,\r
+ Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ switch (Operation) {\r
+ case EdkiiIoMmuOperationBusMasterRead:\r
+ case EdkiiIoMmuOperationBusMasterRead64:\r
+ Attribute = EDKII_IOMMU_ACCESS_READ;\r
+ break;\r
+ case EdkiiIoMmuOperationBusMasterWrite:\r
+ case EdkiiIoMmuOperationBusMasterWrite64:\r
+ Attribute = EDKII_IOMMU_ACCESS_WRITE;\r
+ break;\r
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
+ Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;\r
+ break;\r
+ default:\r
+ ASSERT(FALSE);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ Status = IoMmu->SetAttribute (\r
+ IoMmu,\r
+ *Mapping,\r
+ Attribute\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;\r
+ *Mapping = NULL;\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Completes the Map() operation and releases any corresponding resources.\r
+\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The range was unmapped.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.\r
+**/\r
+EFI_STATUS\r
+IoMmuUnmap (\r
+ IN VOID *Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EDKII_IOMMU_PPI *IoMmu;\r
+\r
+ IoMmu = GetIoMmu ();\r
+\r
+ if (IoMmu != NULL) {\r
+ Status = IoMmu->SetAttribute (IoMmu, Mapping, 0);\r
+ Status = IoMmu->Unmap (IoMmu, Mapping);\r
+ } else {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+ OperationBusMasterCommonBuffer64 mapping.\r
+\r
+ @param Pages The number of pages to allocate.\r
+ @param HostAddress A pointer to store the base system memory address of the\r
+ allocated range.\r
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
+ access the hosts HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were allocated.\r
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuAllocateBuffer (\r
+ IN UINTN Pages,\r
+ OUT VOID **HostAddress,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN NumberOfBytes;\r
+ EFI_PHYSICAL_ADDRESS HostPhyAddress;\r
+ EDKII_IOMMU_PPI *IoMmu;\r
+\r
+ *HostAddress = NULL;\r
+ *DeviceAddress = 0;\r
+\r
+ IoMmu = GetIoMmu ();\r
+\r
+ if (IoMmu != NULL) {\r
+ Status = IoMmu->AllocateBuffer (\r
+ IoMmu,\r
+ EfiBootServicesData,\r
+ Pages,\r
+ HostAddress,\r
+ 0\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ NumberOfBytes = EFI_PAGES_TO_SIZE(Pages);\r
+ Status = IoMmu->Map (\r
+ IoMmu,\r
+ EdkiiIoMmuOperationBusMasterCommonBuffer,\r
+ *HostAddress,\r
+ &NumberOfBytes,\r
+ DeviceAddress,\r
+ Mapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ Status = IoMmu->SetAttribute (\r
+ IoMmu,\r
+ *Mapping,\r
+ EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ } else {\r
+ Status = PeiServicesAllocatePages (\r
+ EfiBootServicesData,\r
+ Pages,\r
+ &HostPhyAddress\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ *HostAddress = (VOID *)(UINTN)HostPhyAddress;\r
+ *DeviceAddress = HostPhyAddress;\r
+ *Mapping = NULL;\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Frees memory that was allocated with AllocateBuffer().\r
+\r
+ @param Pages The number of pages to free.\r
+ @param HostAddress The base system memory address of the allocated range.\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were freed.\r
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
+ was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuFreeBuffer (\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress,\r
+ IN VOID *Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EDKII_IOMMU_PPI *IoMmu;\r
+\r
+ IoMmu = GetIoMmu ();\r
+\r
+ if (IoMmu != NULL) {\r
+ Status = IoMmu->SetAttribute (IoMmu, Mapping, 0);\r
+ Status = IoMmu->Unmap (IoMmu, Mapping);\r
+ Status = IoMmu->FreeBuffer (IoMmu, Pages, HostAddress);\r
+ } else {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+ return Status;\r
+}\r
MdeModulePkg/Bus/Pci/SataControllerDxe/SataControllerDxe.inf\r
MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf\r
MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf\r
+ MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf\r
MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf\r
MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf\r
MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf\r