summaryrefslogtreecommitdiffstats
path: root/SecurityPkg
diff options
context:
space:
mode:
authorEric Dong <eric.dong@intel.com>2016-03-29 14:49:25 +0800
committerFeng Tian <feng.tian@intel.com>2016-03-29 15:37:30 +0800
commitcb274a270344cef20d6012eb873fc71a46931a85 (patch)
tree349b671603387b9dac13525cfaf13c471627b79e /SecurityPkg
parenta06875e1f040dde20dc46fd349cc655c627c3e83 (diff)
downloadedk2-cb274a270344cef20d6012eb873fc71a46931a85.tar.gz
edk2-cb274a270344cef20d6012eb873fc71a46931a85.tar.bz2
edk2-cb274a270344cef20d6012eb873fc71a46931a85.zip
SecurityPkg: OpalPasswordSmm: Add Opal password Smm driver.
This driver used to unlock device in S3 resume phase. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Eric Dong <eric.dong@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
Diffstat (limited to 'SecurityPkg')
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c1267
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h408
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c767
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h173
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c2166
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h456
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h814
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c1036
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h299
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf80
10 files changed, 7466 insertions, 0 deletions
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c
new file mode 100644
index 0000000000..0dc3490a36
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.c
@@ -0,0 +1,1267 @@
+/** @file
+ This driver is used for Opal Password Feature support at AHCI mode.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "OpalPasswordSmm.h"
+
+/**
+ Start command for give slot on specific port.
+
+ @param Port The number of port.
+ @param CommandSlot The number of CommandSlot.
+ @param Timeout The timeout Value of start.
+
+ @retval EFI_DEVICE_ERROR The command start unsuccessfully.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_SUCCESS The command start successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStartCommand (
+ IN UINT8 Port,
+ IN UINT8 CommandSlot,
+ IN UINT64 Timeout
+ );
+
+/**
+ Stop command running for giving port
+
+ @param Port The number of port.
+ @param Timeout The timeout Value of stop.
+
+ @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_SUCCESS The command stop successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStopCommand (
+ IN UINT8 Port,
+ IN UINT64 Timeout
+ );
+
+/**
+ Read AHCI Operation register.
+
+ @param Offset The operation register offset.
+
+ @return The register content read.
+
+**/
+UINT32
+EFIAPI
+AhciReadReg (
+ IN UINT32 Offset
+ )
+{
+ UINT32 Data;
+
+ Data = 0;
+
+ Data = MmioRead32 (mAhciBar + Offset);
+
+ return Data;
+}
+
+/**
+ Write AHCI Operation register.
+
+ @param Offset The operation register offset.
+ @param Data The Data used to write down.
+
+**/
+VOID
+EFIAPI
+AhciWriteReg (
+ IN UINT32 Offset,
+ IN UINT32 Data
+ )
+{
+ MmioWrite32 (mAhciBar + Offset, Data);
+
+ return ;
+}
+
+/**
+ Do AND operation with the Value of AHCI Operation register.
+
+ @param Offset The operation register offset.
+ @param AndData The Data used to do AND operation.
+
+**/
+VOID
+EFIAPI
+AhciAndReg (
+ IN UINT32 Offset,
+ IN UINT32 AndData
+ )
+{
+ UINT32 Data;
+
+ Data = AhciReadReg (Offset);
+
+ Data &= AndData;
+
+ AhciWriteReg (Offset, Data);
+}
+
+/**
+ Do OR operation with the Value of AHCI Operation register.
+
+ @param Offset The operation register offset.
+ @param OrData The Data used to do OR operation.
+
+**/
+VOID
+EFIAPI
+AhciOrReg (
+ IN UINT32 Offset,
+ IN UINT32 OrData
+ )
+{
+ UINT32 Data;
+
+ Data = AhciReadReg (Offset);
+
+ Data |= OrData;
+
+ AhciWriteReg (Offset, Data);
+}
+
+/**
+ Wait for memory set to the test Value.
+
+ @param Offset The memory address to test.
+ @param MaskValue The mask Value of memory.
+ @param TestValue The test Value of memory.
+ @param Timeout The time out Value for wait memory set.
+
+ @retval EFI_DEVICE_ERROR The memory is not set.
+ @retval EFI_TIMEOUT The memory setting is time out.
+ @retval EFI_SUCCESS The memory is correct set.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciWaitMmioSet (
+ IN UINT32 Offset,
+ IN UINT32 MaskValue,
+ IN UINT32 TestValue,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Value;
+ UINT32 Delay;
+
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+
+ do {
+ Value = AhciReadReg (Offset) & MaskValue;
+
+ if (Value == TestValue) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay (100);
+
+ Delay--;
+
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+}
+/**
+ Wait for the Value of the specified system memory set to the test Value.
+
+ @param Address The system memory address to test.
+ @param MaskValue The mask Value of memory.
+ @param TestValue The test Value of memory.
+ @param Timeout The time out Value for wait memory set, uses 100ns as a unit.
+
+ @retval EFI_TIMEOUT The system memory setting is time out.
+ @retval EFI_SUCCESS The system memory is correct set.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciWaitMemSet (
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN UINT32 MaskValue,
+ IN UINT32 TestValue,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Value;
+ UINT32 Delay;
+
+ Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
+
+ do {
+ //
+ // Access sytem memory to see if the Value is the tested one.
+ //
+ // The system memory pointed by Address will be updated by the
+ // SATA Host Controller, "volatile" is introduced to prevent
+ // compiler from optimizing the access to the memory address
+ // to only read once.
+ //
+ Value = *(volatile UINT32 *) (UINTN) Address;
+ Value &= MaskValue;
+
+ if (Value == TestValue) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay (100);
+
+ Delay--;
+
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Check the memory status to the test Value.
+
+ @param[in] Address The memory address to test.
+ @param[in] MaskValue The mask Value of memory.
+ @param[in] TestValue The test Value of memory.
+ @param[in, out] RetryTimes The retry times Value for waitting memory set. If 0, then just try once.
+
+ @retval EFI_NOTREADY The memory is not set.
+ @retval EFI_TIMEOUT The memory setting retry times out.
+ @retval EFI_SUCCESS The memory is correct set.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciCheckMemSet (
+ IN UINTN Address,
+ IN UINT32 MaskValue,
+ IN UINT32 TestValue,
+ IN OUT UINTN *RetryTimes OPTIONAL
+ )
+{
+ UINT32 Value;
+
+ if (RetryTimes != NULL) {
+ (*RetryTimes)--;
+ }
+
+ Value = *(volatile UINT32 *) Address;
+ Value &= MaskValue;
+
+ if (Value == TestValue) {
+ return EFI_SUCCESS;
+ }
+
+ if ((RetryTimes != NULL) && (*RetryTimes == 0)) {
+ return EFI_TIMEOUT;
+ } else {
+ return EFI_NOT_READY;
+ }
+}
+
+/**
+ Clear the port interrupt and error status. It will also clear
+ HBA interrupt status.
+
+ @param Port The number of port.
+
+**/
+VOID
+EFIAPI
+AhciClearPortStatus (
+ IN UINT8 Port
+ )
+{
+ UINT32 Offset;
+
+ //
+ // Clear any error status
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
+ AhciWriteReg (Offset, AhciReadReg (Offset));
+
+ //
+ // Clear any port interrupt status
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
+ AhciWriteReg (Offset, AhciReadReg (Offset));
+
+ //
+ // Clear any HBA interrupt status
+ //
+ AhciWriteReg (EFI_AHCI_IS_OFFSET, AhciReadReg (EFI_AHCI_IS_OFFSET));
+}
+
+/**
+ Enable the FIS running for giving port.
+
+ @param Port The number of port.
+ @param Timeout The timeout Value of enabling FIS.
+
+ @retval EFI_DEVICE_ERROR The FIS enable setting fails.
+ @retval EFI_TIMEOUT The FIS enable setting is time out.
+ @retval EFI_SUCCESS The FIS enable successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciEnableFisReceive (
+ IN UINT8 Port,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Offset;
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ AhciOrReg (Offset, EFI_AHCI_PORT_CMD_FRE);
+
+ return AhciWaitMmioSet (
+ Offset,
+ EFI_AHCI_PORT_CMD_FR,
+ EFI_AHCI_PORT_CMD_FR,
+ Timeout
+ );
+}
+
+/**
+ Disable the FIS running for giving port.
+
+ @param Port The number of port.
+ @param Timeout The timeout Value of disabling FIS.
+
+ @retval EFI_DEVICE_ERROR The FIS disable setting fails.
+ @retval EFI_TIMEOUT The FIS disable setting is time out.
+ @retval EFI_UNSUPPORTED The port is in running state.
+ @retval EFI_SUCCESS The FIS disable successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciDisableFisReceive (
+ IN UINT8 Port,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Offset;
+ UINT32 Data;
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ Data = AhciReadReg (Offset);
+
+ //
+ // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
+ //
+ if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Check if the Fis receive DMA engine for the port is running.
+ //
+ if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {
+ return EFI_SUCCESS;
+ }
+
+ AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));
+
+ return AhciWaitMmioSet (
+ Offset,
+ EFI_AHCI_PORT_CMD_FR,
+ 0,
+ Timeout
+ );
+}
+
+/**
+ Build the command list, command table and prepare the fis receiver.
+
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The timeout Value of stop.
+ @param CommandFis The control fis will be used for the transfer.
+ @param CommandList The command list will be used for the transfer.
+ @param AtapiCommand The atapi command will be used for the transfer.
+ @param AtapiCommandLength The Length of the atapi command.
+ @param CommandSlotNumber The command slot will be used for the transfer.
+ @param DataPhysicalAddr The pointer to the Data Buffer pci bus master address.
+ @param DataLength The Data count to be transferred.
+
+**/
+VOID
+EFIAPI
+AhciBuildCommand (
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN EFI_AHCI_COMMAND_FIS *CommandFis,
+ IN EFI_AHCI_COMMAND_LIST *CommandList,
+ IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
+ IN UINT8 AtapiCommandLength,
+ IN UINT8 CommandSlotNumber,
+ IN OUT VOID *DataPhysicalAddr,
+ IN UINT64 DataLength
+ )
+{
+ UINT64 BaseAddr;
+ UINT64 PrdtNumber;
+ UINTN RemainedData;
+ UINTN MemAddr;
+ DATA_64 Data64;
+ UINT32 Offset;
+
+ //
+ // Filling the PRDT
+ //
+ PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT);
+
+ //
+ // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.
+ // It also limits that the maximum amount of the PRDT entry in the command table
+ // is 65535.
+ //
+ ASSERT (PrdtNumber <= 1);
+
+ Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis);
+
+ BaseAddr = Data64.Uint64;
+
+ ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
+
+ ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));
+
+ CommandFis->AhciCFisPmNum = PortMultiplier;
+
+ CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ if (AtapiCommand != NULL) {
+ CopyMem (
+ &AhciRegisters->AhciCommandTable->AtapiCmd,
+ AtapiCommand,
+ AtapiCommandLength
+ );
+
+ CommandList->AhciCmdA = 1;
+ CommandList->AhciCmdP = 1;
+
+ AhciOrReg (Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
+ } else {
+ AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
+ }
+
+ RemainedData = (UINTN) DataLength;
+ MemAddr = (UINTN) DataPhysicalAddr;
+ CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber;
+
+ AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1;
+
+ Data64.Uint64 = (UINT64)MemAddr;
+ AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba = Data64.Uint32.Lower32;
+ AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32;
+
+ //
+ // Set the last PRDT to Interrupt On Complete
+ //
+ AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1;
+
+ CopyMem (
+ (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),
+ CommandList,
+ sizeof (EFI_AHCI_COMMAND_LIST)
+ );
+
+ Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable;
+ AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;
+ AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;
+ AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;
+
+}
+
+/**
+ Buid a command FIS.
+
+ @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure.
+ @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure.
+
+**/
+VOID
+EFIAPI
+AhciBuildCommandFis (
+ IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock
+ )
+{
+ ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));
+
+ CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;
+ //
+ // Indicator it's a command
+ //
+ CmdFis->AhciCFisCmdInd = 0x1;
+ CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;
+
+ CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;
+ CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;
+
+ CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;
+ CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;
+
+ CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;
+ CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;
+
+ CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;
+ CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;
+
+ CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;
+ CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;
+
+ CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);
+}
+
+/**
+ Start a PIO Data transfer on specific port.
+
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The timeout Value of stop.
+ @param AtapiCommand The atapi command will be used for the transfer.
+ @param AtapiCommandLength The Length of the atapi command.
+ @param Read The transfer direction.
+ @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data.
+ @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data.
+ @param MemoryAddr The pointer to the Data Buffer.
+ @param DataCount The Data count to be transferred.
+ @param Timeout The timeout Value of non Data transfer.
+
+ @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_UNSUPPORTED The device is not ready for transfer.
+ @retval EFI_SUCCESS The PIO Data transfer executes successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciPioTransfer (
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
+ IN UINT8 AtapiCommandLength,
+ IN BOOLEAN Read,
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
+ IN OUT VOID *MemoryAddr,
+ IN UINT32 DataCount,
+ IN UINT64 Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINT32 FisBaseAddr;
+ UINT32 Offset;
+ UINT32 Delay;
+ EFI_AHCI_COMMAND_FIS CFis;
+ EFI_AHCI_COMMAND_LIST CmdList;
+ UINT32 PortTfd;
+ UINT32 PrdCount;
+ UINT32 OldRfisLo;
+ UINT32 OldRfisHi;
+ UINT32 OldCmdListLo;
+ UINT32 OldCmdListHi;
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
+ OldRfisLo = AhciReadReg (Offset);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
+ OldRfisHi = AhciReadReg (Offset);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
+ AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
+ AhciWriteReg (Offset, 0);
+
+ //
+ // Single task envrionment, we only use one command table for all port
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
+ OldCmdListLo = AhciReadReg (Offset);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
+ OldCmdListHi = AhciReadReg (Offset);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
+ AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
+ AhciWriteReg (Offset, 0);
+
+ //
+ // Package read needed
+ //
+ AhciBuildCommandFis (&CFis, AtaCommandBlock);
+
+ ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
+
+ CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
+ CmdList.AhciCmdW = Read ? 0 : 1;
+
+ AhciBuildCommand (
+ AhciRegisters,
+ Port,
+ PortMultiplier,
+ &CFis,
+ &CmdList,
+ AtapiCommand,
+ AtapiCommandLength,
+ 0,
+ (VOID *)(UINTN)MemoryAddr,
+ DataCount
+ );
+
+ Status = AhciStartCommand (
+ Port,
+ 0,
+ Timeout
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Checking the status and wait the driver sending Data
+ //
+ FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis;
+ if (Read && (AtapiCommand == 0)) {
+ //
+ // Wait device sends the PIO setup fis before Data transfer
+ //
+ Status = EFI_TIMEOUT;
+ Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
+ do {
+ Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;
+
+ Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);
+ if (!EFI_ERROR (Status)) {
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
+ PortTfd = AhciReadReg ((UINT32) Offset);
+ //
+ // PxTFD will be updated if there is a D2H or SetupFIS received.
+ // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.
+ //
+ if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
+ if (PrdCount == DataCount) {
+ break;
+ }
+ }
+
+ Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
+ Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);
+ if (!EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay(100);
+
+ Delay--;
+ } while (Delay > 0);
+ } else {
+ //
+ // Wait for D2H Fis is received
+ //
+ Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
+ Status = AhciWaitMemSet (
+ Offset,
+ EFI_AHCI_FIS_TYPE_MASK,
+ EFI_AHCI_FIS_REGISTER_D2H,
+ Timeout
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
+ PortTfd = AhciReadReg ((UINT32) Offset);
+ if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+Exit:
+ AhciStopCommand (
+ Port,
+ Timeout
+ );
+
+ AhciDisableFisReceive (
+ Port,
+ Timeout
+ );
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
+ AhciWriteReg (Offset, OldRfisLo);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
+ AhciWriteReg (Offset, OldRfisHi);
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
+ AhciWriteReg (Offset, OldCmdListLo);
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
+ AhciWriteReg (Offset, OldCmdListHi);
+
+ return Status;
+}
+
+/**
+ Stop command running for giving port
+
+ @param Port The number of port.
+ @param Timeout The timeout Value of stop.
+
+ @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_SUCCESS The command stop successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStopCommand (
+ IN UINT8 Port,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Offset;
+ UINT32 Data;
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ Data = AhciReadReg (Offset);
+
+ if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {
+ AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));
+ }
+
+ return AhciWaitMmioSet (
+ Offset,
+ EFI_AHCI_PORT_CMD_CR,
+ 0,
+ Timeout
+ );
+}
+
+/**
+ Start command for give slot on specific port.
+
+ @param Port The number of port.
+ @param CommandSlot The number of CommandSlot.
+ @param Timeout The timeout Value of start.
+
+ @retval EFI_DEVICE_ERROR The command start unsuccessfully.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_SUCCESS The command start successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciStartCommand (
+ IN UINT8 Port,
+ IN UINT8 CommandSlot,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 CmdSlotBit;
+ EFI_STATUS Status;
+ UINT32 PortStatus;
+ UINT32 StartCmd;
+ UINT32 PortTfd;
+ UINT32 Offset;
+ UINT32 Capability;
+
+ //
+ // Collect AHCI controller information
+ //
+ Capability = AhciReadReg(EFI_AHCI_CAPABILITY_OFFSET);
+
+ CmdSlotBit = (UINT32) (1 << CommandSlot);
+
+ AhciClearPortStatus (
+ Port
+ );
+
+ Status = AhciEnableFisReceive (
+ Port,
+ Timeout
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ PortStatus = AhciReadReg (Offset);
+
+ StartCmd = 0;
+ if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {
+ StartCmd = AhciReadReg (Offset);
+ StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;
+ StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;
+ }
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
+ PortTfd = AhciReadReg (Offset);
+
+ if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {
+ if ((Capability & BIT24) != 0) {
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ AhciOrReg (Offset, EFI_AHCI_PORT_CMD_COL);
+
+ AhciWaitMmioSet (
+ Offset,
+ EFI_AHCI_PORT_CMD_COL,
+ 0,
+ Timeout
+ );
+ }
+ }
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ AhciOrReg (Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);
+
+ //
+ // Setting the command
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;
+ AhciAndReg (Offset, 0);
+ AhciOrReg (Offset, CmdSlotBit);
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
+ AhciAndReg (Offset, 0);
+ AhciOrReg (Offset, CmdSlotBit);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Do AHCI HBA reset.
+
+ @param[in] Timeout The timeout Value of reset.
+
+ @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.
+ @retval EFI_TIMEOUT The reset operation is time out.
+ @retval EFI_SUCCESS AHCI controller is reset successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciReset (
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Delay;
+ UINT32 Value;
+ UINT32 Capability;
+
+ //
+ // Collect AHCI controller information
+ //
+ Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
+
+ //
+ // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
+ //
+ if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
+ AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
+ }
+
+ AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);
+
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+
+ do {
+ Value = AhciReadReg(EFI_AHCI_GHC_OFFSET);
+ if ((Value & EFI_AHCI_GHC_RESET) == 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay(100);
+
+ Delay--;
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+
+
+}
+
+/**
+ Send Buffer cmd to specific device.
+
+ @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param[in] Port The port number of attached ATA device.
+ @param[in] PortMultiplier The port number of port multiplier of attached ATA device.
+ @param[in, out] Buffer The Data Buffer to store IDENTIFY PACKET Data.
+
+ @retval EFI_DEVICE_ERROR The cmd abort with error occurs.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_UNSUPPORTED The device is not ready for executing.
+ @retval EFI_SUCCESS The cmd executes successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciIdentify (
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN OUT ATA_IDENTIFY_DATA *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
+
+ if (AhciRegisters == NULL || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
+
+ AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;
+ AtaCommandBlock.AtaSectorCount = 1;
+
+ Status = AhciPioTransfer (
+ AhciRegisters,
+ Port,
+ PortMultiplier,
+ NULL,
+ 0,
+ TRUE,
+ &AtaCommandBlock,
+ NULL,
+ Buffer,
+ sizeof (ATA_IDENTIFY_DATA),
+ ATA_TIMEOUT
+ );
+
+ return Status;
+}
+
+/**
+ Get AHCI mode MMIO Bar Size.
+
+ @param[in] Bus The bus number of ata host controller.
+ @param[in] Device The device number of ata host controller.
+ @param[in] Function The function number of ata host controller.
+
+ @retval The Size of AHCI MMIO BAR.
+
+**/
+UINT32
+EFIAPI
+GetAhciBarSize (
+ IN UINTN Bus,
+ IN UINTN Device,
+ IN UINTN Function
+ )
+{
+ UINT32 Size;
+ UINT32 OldBar;
+
+ OldBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
+ //
+ // Disable PCI CMD.MSE bit before calculating MMIO Bar Size as it needs write all 1 to BAR register.
+ //
+ PciAnd32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), (UINT32)~BIT1);
+
+ //
+ // Get AHCI MMIO Bar Size.
+ //
+ PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), 0xFFFFFFFF);
+ Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
+ Size = (~(Size & 0xFFFFFFF0)) + 1;
+
+ //
+ // Restore old MMIO Bar.
+ //
+ PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), OldBar);
+ //
+ // Enable PCI CMD.MSE bit after restoring MMIO Bar.
+ //
+ PciOr32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), BIT1);
+
+ return Size;
+}
+
+/**
+ Get AHCI mode base address registers' Value.
+
+ @param[in] Bus The bus number of ata host controller.
+ @param[in] Device The device number of ata host controller.
+ @param[in] Function The function number of ata host controller.
+
+ @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
+ @retval EFI_SUCCESS Get the Base address successfully
+ @retval Other Read the pci configureation Data error
+
+**/
+EFI_STATUS
+EFIAPI
+GetAhciBaseAddress (
+ IN UINTN Bus,
+ IN UINTN Device,
+ IN UINTN Function
+ )
+{
+ UINT32 Size;
+
+ //
+ // Get AHCI MMIO Bar
+ //
+ mAhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
+ //
+ // Get AHCI MMIO Bar Size
+ //
+ Size = GetAhciBarSize (Bus, Device, Function);
+ //
+ // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.
+ //
+ if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)mAhciBar, Size)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate transfer-related Data struct which is used at AHCI mode.
+
+ @retval EFI_OUT_OF_RESOURCE The allocation is failure.
+ @retval EFI_SUCCESS Successful to allocate memory.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAllocateResource (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Base;
+
+ //
+ // Allocate resources required by AHCI host controller.
+ //
+ Base = 0xFFFFFFFF;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),
+ &Base
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
+ mAhciRegisters.AhciRFis = (VOID *)(UINTN)Base;
+
+ Base = 0xFFFFFFFF;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),
+ &Base
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
+ mAhciRegisters.AhciCmdList = (VOID *)(UINTN)Base;
+
+ Base = 0xFFFFFFFF;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),
+ &Base
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
+ mAhciRegisters.AhciCommandTable = (VOID *)(UINTN)Base;
+ return EFI_SUCCESS;
+}
+
+/**
+ Free allocated transfer-related Data struct which is used at AHCI mode.
+
+**/
+VOID
+EFIAPI
+AhciFreeResource (
+ VOID
+ )
+{
+ if (mAhciRegisters.AhciRFis != NULL) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
+ }
+
+ if (mAhciRegisters.AhciCmdList != NULL) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
+ }
+
+ if (mAhciRegisters.AhciCommandTable != NULL) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCommandTable, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
+ }
+}
+
+/**
+ Initialize ATA host controller at AHCI mode.
+
+ The function is designed to initialize ATA host controller.
+
+ @param[in] Port The port number to do initialization.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciModeInitialize (
+ UINT8 Port
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Capability;
+ UINT32 Offset;
+ UINT32 Data;
+ UINT32 PhyDetectDelay;
+
+ Status = AhciReset (ATA_TIMEOUT);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Collect AHCI controller information
+ //
+ Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
+
+ //
+ // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
+ //
+ if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
+ AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
+ }
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
+ AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciRFis);
+
+ //
+ // Single task envrionment, we only use one command table for all port
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
+ AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciCmdList);
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
+ Data = AhciReadReg (Offset);
+ if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {
+ AhciOrReg (Offset, EFI_AHCI_PORT_CMD_POD);
+ }
+
+ if ((Capability & BIT27) != 0) {
+ AhciOrReg (Offset, EFI_AHCI_PORT_CMD_SUD);
+ }
+
+ //
+ // Disable aggressive power management.
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
+ AhciOrReg (Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);
+ //
+ // Disable the reporting of the corresponding interrupt to system software.
+ //
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;
+ AhciAndReg (Offset, 0);
+
+ Status = AhciEnableFisReceive (
+ Port,
+ EFI_TIMER_PERIOD_MILLISECONDS(500)
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
+ // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
+ //
+ PhyDetectDelay = 16 * 1000;
+ do {
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
+ if (AhciReadReg(Offset) != 0) {
+ AhciWriteReg (Offset, AhciReadReg(Offset));
+ }
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
+
+ Data = AhciReadReg (Offset) & EFI_AHCI_PORT_TFD_MASK;
+ if (Data == 0) {
+ break;
+ }
+
+ MicroSecondDelay (1000);
+ PhyDetectDelay--;
+ } while (PhyDetectDelay > 0);
+
+ if (PhyDetectDelay == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;
+ Status = AhciWaitMmioSet (
+ Offset,
+ 0x0000FFFF,
+ 0x00000101,
+ EFI_TIMER_PERIOD_SECONDS(16)
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h
new file mode 100644
index 0000000000..3a7f6331ca
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalAhciMode.h
@@ -0,0 +1,408 @@
+/** @file
+ Header file for AHCI mode of ATA host controller.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#ifndef __OPAL_PASSWORD_AHCI_MODE_H__
+#define __OPAL_PASSWORD_AHCI_MODE_H__
+
+//
+// OPAL LIBRARY CALLBACKS
+//
+#define ATA_COMMAND_TRUSTED_RECEIVE 0x5C
+#define ATA_COMMAND_TRUSTED_SEND 0x5E
+
+//
+// ATA TRUSTED commands express transfer Length in 512 byte multiple
+//
+#define ATA_TRUSTED_TRANSFER_LENGTH_MULTIPLE 512
+#define ATA_DEVICE_LBA 0x40 ///< Set for commands with LBA (rather than CHS) addresses
+
+
+#define EFI_AHCI_BAR_INDEX 0x05
+
+#define EFI_AHCI_CAPABILITY_OFFSET 0x0000
+#define EFI_AHCI_CAP_SAM BIT18
+#define EFI_AHCI_GHC_OFFSET 0x0004
+#define EFI_AHCI_GHC_RESET BIT0
+#define EFI_AHCI_GHC_IE BIT1
+#define EFI_AHCI_GHC_ENABLE BIT31
+#define EFI_AHCI_IS_OFFSET 0x0008
+#define EFI_AHCI_PI_OFFSET 0x000C
+
+typedef struct {
+ UINT32 Lower32;
+ UINT32 Upper32;
+} DATA_32;
+
+typedef union {
+ DATA_32 Uint32;
+ UINT64 Uint64;
+} DATA_64;
+
+//
+// Each PRDT entry can point to a memory block up to 4M byte
+//
+#define EFI_AHCI_MAX_DATA_PER_PRDT 0x400000
+
+#define EFI_AHCI_FIS_REGISTER_H2D 0x27 //Register FIS - Host to Device
+#define EFI_AHCI_FIS_REGISTER_H2D_LENGTH 20
+#define EFI_AHCI_FIS_REGISTER_D2H 0x34 //Register FIS - Device to Host
+#define EFI_AHCI_FIS_REGISTER_D2H_LENGTH 20
+#define EFI_AHCI_FIS_DMA_ACTIVATE 0x39 //DMA Activate FIS - Device to Host
+#define EFI_AHCI_FIS_DMA_ACTIVATE_LENGTH 4
+#define EFI_AHCI_FIS_DMA_SETUP 0x41 //DMA Setup FIS - Bi-directional
+#define EFI_AHCI_FIS_DMA_SETUP_LENGTH 28
+#define EFI_AHCI_FIS_DATA 0x46 //Data FIS - Bi-directional
+#define EFI_AHCI_FIS_BIST 0x58 //BIST Activate FIS - Bi-directional
+#define EFI_AHCI_FIS_BIST_LENGTH 12
+#define EFI_AHCI_FIS_PIO_SETUP 0x5F //PIO Setup FIS - Device to Host
+#define EFI_AHCI_FIS_PIO_SETUP_LENGTH 20
+#define EFI_AHCI_FIS_SET_DEVICE 0xA1 //Set Device Bits FIS - Device to Host
+#define EFI_AHCI_FIS_SET_DEVICE_LENGTH 8
+
+#define EFI_AHCI_D2H_FIS_OFFSET 0x40
+#define EFI_AHCI_DMA_FIS_OFFSET 0x00
+#define EFI_AHCI_PIO_FIS_OFFSET 0x20
+#define EFI_AHCI_SDB_FIS_OFFSET 0x58
+#define EFI_AHCI_FIS_TYPE_MASK 0xFF
+#define EFI_AHCI_U_FIS_OFFSET 0x60
+
+//
+// Port register
+//
+#define EFI_AHCI_PORT_START 0x0100
+#define EFI_AHCI_PORT_REG_WIDTH 0x0080
+#define EFI_AHCI_PORT_CLB 0x0000
+#define EFI_AHCI_PORT_CLBU 0x0004
+#define EFI_AHCI_PORT_FB 0x0008
+#define EFI_AHCI_PORT_FBU 0x000C
+#define EFI_AHCI_PORT_IS 0x0010
+#define EFI_AHCI_PORT_IS_DHRS BIT0
+#define EFI_AHCI_PORT_IS_PSS BIT1
+#define EFI_AHCI_PORT_IS_SSS BIT2
+#define EFI_AHCI_PORT_IS_SDBS BIT3
+#define EFI_AHCI_PORT_IS_UFS BIT4
+#define EFI_AHCI_PORT_IS_DPS BIT5
+#define EFI_AHCI_PORT_IS_PCS BIT6
+#define EFI_AHCI_PORT_IS_DIS BIT7
+#define EFI_AHCI_PORT_IS_PRCS BIT22
+#define EFI_AHCI_PORT_IS_IPMS BIT23
+#define EFI_AHCI_PORT_IS_OFS BIT24
+#define EFI_AHCI_PORT_IS_INFS BIT26
+#define EFI_AHCI_PORT_IS_IFS BIT27
+#define EFI_AHCI_PORT_IS_HBDS BIT28
+#define EFI_AHCI_PORT_IS_HBFS BIT29
+#define EFI_AHCI_PORT_IS_TFES BIT30
+#define EFI_AHCI_PORT_IS_CPDS BIT31
+#define EFI_AHCI_PORT_IS_CLEAR 0xFFFFFFFF
+#define EFI_AHCI_PORT_IS_FIS_CLEAR 0x0000000F
+
+#define EFI_AHCI_PORT_IE 0x0014
+#define EFI_AHCI_PORT_CMD 0x0018
+#define EFI_AHCI_PORT_CMD_ST_MASK 0xFFFFFFFE
+#define EFI_AHCI_PORT_CMD_ST BIT0
+#define EFI_AHCI_PORT_CMD_SUD BIT1
+#define EFI_AHCI_PORT_CMD_POD BIT2
+#define EFI_AHCI_PORT_CMD_COL BIT3
+#define EFI_AHCI_PORT_CMD_CR BIT15
+#define EFI_AHCI_PORT_CMD_FRE BIT4
+#define EFI_AHCI_PORT_CMD_FR BIT14
+#define EFI_AHCI_PORT_CMD_MASK ~(EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_FRE | EFI_AHCI_PORT_CMD_COL)
+#define EFI_AHCI_PORT_CMD_PMA BIT17
+#define EFI_AHCI_PORT_CMD_HPCP BIT18
+#define EFI_AHCI_PORT_CMD_MPSP BIT19
+#define EFI_AHCI_PORT_CMD_CPD BIT20
+#define EFI_AHCI_PORT_CMD_ESP BIT21
+#define EFI_AHCI_PORT_CMD_ATAPI BIT24
+#define EFI_AHCI_PORT_CMD_DLAE BIT25
+#define EFI_AHCI_PORT_CMD_ALPE BIT26
+#define EFI_AHCI_PORT_CMD_ASP BIT27
+#define EFI_AHCI_PORT_CMD_ICC_MASK (BIT28 | BIT29 | BIT30 | BIT31)
+#define EFI_AHCI_PORT_CMD_ACTIVE (1 << 28 )
+#define EFI_AHCI_PORT_TFD 0x0020
+#define EFI_AHCI_PORT_TFD_MASK (BIT7 | BIT3 | BIT0)
+#define EFI_AHCI_PORT_TFD_BSY BIT7
+#define EFI_AHCI_PORT_TFD_DRQ BIT3
+#define EFI_AHCI_PORT_TFD_ERR BIT0
+#define EFI_AHCI_PORT_TFD_ERR_MASK 0x00FF00
+#define EFI_AHCI_PORT_SIG 0x0024
+#define EFI_AHCI_PORT_SSTS 0x0028
+#define EFI_AHCI_PORT_SSTS_DET_MASK 0x000F
+#define EFI_AHCI_PORT_SSTS_DET 0x0001
+#define EFI_AHCI_PORT_SSTS_DET_PCE 0x0003
+#define EFI_AHCI_PORT_SSTS_SPD_MASK 0x00F0
+#define EFI_AHCI_PORT_SCTL 0x002C
+#define EFI_AHCI_PORT_SCTL_DET_MASK 0x000F
+#define EFI_AHCI_PORT_SCTL_MASK (~EFI_AHCI_PORT_SCTL_DET_MASK)
+#define EFI_AHCI_PORT_SCTL_DET_INIT 0x0001
+#define EFI_AHCI_PORT_SCTL_DET_PHYCOMM 0x0003
+#define EFI_AHCI_PORT_SCTL_SPD_MASK 0x00F0
+#define EFI_AHCI_PORT_SCTL_IPM_MASK 0x0F00
+#define EFI_AHCI_PORT_SCTL_IPM_INIT 0x0300
+#define EFI_AHCI_PORT_SCTL_IPM_PSD 0x0100
+#define EFI_AHCI_PORT_SCTL_IPM_SSD 0x0200
+#define EFI_AHCI_PORT_SERR 0x0030
+#define EFI_AHCI_PORT_SERR_RDIE BIT0
+#define EFI_AHCI_PORT_SERR_RCE BIT1
+#define EFI_AHCI_PORT_SERR_TDIE BIT8
+#define EFI_AHCI_PORT_SERR_PCDIE BIT9
+#define EFI_AHCI_PORT_SERR_PE BIT10
+#define EFI_AHCI_PORT_SERR_IE BIT11
+#define EFI_AHCI_PORT_SERR_PRC BIT16
+#define EFI_AHCI_PORT_SERR_PIE BIT17
+#define EFI_AHCI_PORT_SERR_CW BIT18
+#define EFI_AHCI_PORT_SERR_BDE BIT19
+#define EFI_AHCI_PORT_SERR_DE BIT20
+#define EFI_AHCI_PORT_SERR_CRCE BIT21
+#define EFI_AHCI_PORT_SERR_HE BIT22
+#define EFI_AHCI_PORT_SERR_LSE BIT23
+#define EFI_AHCI_PORT_SERR_TSTE BIT24
+#define EFI_AHCI_PORT_SERR_UFT BIT25
+#define EFI_AHCI_PORT_SERR_EX BIT26
+#define EFI_AHCI_PORT_ERR_CLEAR 0xFFFFFFFF
+#define EFI_AHCI_PORT_SACT 0x0034
+#define EFI_AHCI_PORT_CI 0x0038
+#define EFI_AHCI_PORT_SNTF 0x003C
+
+
+#pragma pack(1)
+//
+// Command List structure includes total 32 entries.
+// The entry Data structure is listed at the following.
+//
+typedef struct {
+ UINT32 AhciCmdCfl:5; //Command FIS Length
+ UINT32 AhciCmdA:1; //ATAPI
+ UINT32 AhciCmdW:1; //Write
+ UINT32 AhciCmdP:1; //Prefetchable
+ UINT32 AhciCmdR:1; //Reset
+ UINT32 AhciCmdB:1; //BIST
+ UINT32 AhciCmdC:1; //Clear Busy upon R_OK
+ UINT32 AhciCmdRsvd:1;
+ UINT32 AhciCmdPmp:4; //Port Multiplier Port
+ UINT32 AhciCmdPrdtl:16; //Physical Region Descriptor Table Length
+ UINT32 AhciCmdPrdbc; //Physical Region Descriptor Byte Count
+ UINT32 AhciCmdCtba; //Command Table Descriptor Base Address
+ UINT32 AhciCmdCtbau; //Command Table Descriptor Base Address Upper 32-BITs
+ UINT32 AhciCmdRsvd1[4];
+} EFI_AHCI_COMMAND_LIST;
+
+//
+// This is a software constructed FIS.
+// For Data transfer operations, this is the H2D Register FIS format as
+// specified in the Serial ATA Revision 2.6 specification.
+//
+typedef struct {
+ UINT8 AhciCFisType;
+ UINT8 AhciCFisPmNum:4;
+ UINT8 AhciCFisRsvd:1;
+ UINT8 AhciCFisRsvd1:1;
+ UINT8 AhciCFisRsvd2:1;
+ UINT8 AhciCFisCmdInd:1;
+ UINT8 AhciCFisCmd;
+ UINT8 AhciCFisFeature;
+ UINT8 AhciCFisSecNum;
+ UINT8 AhciCFisClyLow;
+ UINT8 AhciCFisClyHigh;
+ UINT8 AhciCFisDevHead;
+ UINT8 AhciCFisSecNumExp;
+ UINT8 AhciCFisClyLowExp;
+ UINT8 AhciCFisClyHighExp;
+ UINT8 AhciCFisFeatureExp;
+ UINT8 AhciCFisSecCount;
+ UINT8 AhciCFisSecCountExp;
+ UINT8 AhciCFisRsvd3;
+ UINT8 AhciCFisControl;
+ UINT8 AhciCFisRsvd4[4];
+ UINT8 AhciCFisRsvd5[44];
+} EFI_AHCI_COMMAND_FIS;
+
+//
+// ACMD: ATAPI command (12 or 16 bytes)
+//
+typedef struct {
+ UINT8 AtapiCmd[0x10];
+} EFI_AHCI_ATAPI_COMMAND;
+
+//
+// Physical Region Descriptor Table includes up to 65535 entries
+// The entry Data structure is listed at the following.
+// the actual entry number comes from the PRDTL field in the command
+// list entry for this command slot.
+//
+typedef struct {
+ UINT32 AhciPrdtDba; //Data Base Address
+ UINT32 AhciPrdtDbau; //Data Base Address Upper 32-BITs
+ UINT32 AhciPrdtRsvd;
+ UINT32 AhciPrdtDbc:22; //Data Byte Count
+ UINT32 AhciPrdtRsvd1:9;
+ UINT32 AhciPrdtIoc:1; //Interrupt on Completion
+} EFI_AHCI_COMMAND_PRDT;
+
+//
+// Command table Data strucute which is pointed to by the entry in the command list
+//
+typedef struct {
+ EFI_AHCI_COMMAND_FIS CommandFis; // A software constructed FIS.
+ EFI_AHCI_ATAPI_COMMAND AtapiCmd; // 12 or 16 bytes ATAPI cmd.
+ UINT8 Reserved[0x30];
+ EFI_AHCI_COMMAND_PRDT PrdtTable; // The scatter/gather list for Data transfer
+} EFI_AHCI_COMMAND_TABLE;
+
+//
+// Received FIS structure
+//
+typedef struct {
+ UINT8 AhciDmaSetupFis[0x1C]; // Dma Setup Fis: offset 0x00
+ UINT8 AhciDmaSetupFisRsvd[0x04];
+ UINT8 AhciPioSetupFis[0x14]; // Pio Setup Fis: offset 0x20
+ UINT8 AhciPioSetupFisRsvd[0x0C];
+ UINT8 AhciD2HRegisterFis[0x14]; // D2H Register Fis: offset 0x40
+ UINT8 AhciD2HRegisterFisRsvd[0x04];
+ UINT64 AhciSetDeviceBitsFis; // Set Device Bits Fix: offset 0x58
+ UINT8 AhciUnknownFis[0x40]; // Unkonwn Fis: offset 0x60
+ UINT8 AhciUnknownFisRsvd[0x60];
+} EFI_AHCI_RECEIVED_FIS;
+
+#pragma pack()
+
+typedef struct {
+ EFI_AHCI_RECEIVED_FIS *AhciRFis;
+ EFI_AHCI_COMMAND_LIST *AhciCmdList;
+ EFI_AHCI_COMMAND_TABLE *AhciCommandTable;
+} EFI_AHCI_REGISTERS;
+
+extern EFI_AHCI_REGISTERS mAhciRegisters;
+extern UINT32 mAhciBar;
+
+/**
+ Send Buffer cmd to specific device.
+
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The timeout Value of stop.
+ @param Buffer The Data Buffer to store IDENTIFY PACKET Data.
+
+ @retval EFI_DEVICE_ERROR The cmd abort with error occurs.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_UNSUPPORTED The device is not ready for executing.
+ @retval EFI_SUCCESS The cmd executes successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciIdentify (
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN OUT ATA_IDENTIFY_DATA *Buffer
+ );
+
+/**
+ Get AHCI mode base address registers' Value.
+
+ @param[in] Bus The bus number of ata host controller.
+ @param[in] Device The device number of ata host controller.
+ @param[in] Function The function number of ata host controller.
+
+ @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
+ @retval EFI_SUCCESS Get the Base address successfully
+ @retval Other Read the pci configureation Data error
+
+**/
+EFI_STATUS
+EFIAPI
+GetAhciBaseAddress (
+ IN UINTN Bus,
+ IN UINTN Device,
+ IN UINTN Function
+ );
+
+/**
+ Allocate transfer-related Data struct which is used at AHCI mode.
+
+ @retval EFI_OUT_OF_RESOURCE The allocation is failure.
+ @retval EFI_SUCCESS Successful to allocate memory.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAllocateResource (
+ VOID
+ );
+
+/**
+ Free allocated transfer-related Data struct which is used at AHCI mode.
+
+**/
+VOID
+EFIAPI
+AhciFreeResource (
+ VOID
+ );
+
+/**
+ Initialize ATA host controller at AHCI mode.
+
+ The function is designed to initialize ATA host controller.
+
+ @param[in] Port The port number to do initialization.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciModeInitialize (
+ UINT8 Port
+ );
+
+/**
+ Start a PIO Data transfer on specific port.
+
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
+ @param Port The number of port.
+ @param PortMultiplier The timeout Value of stop.
+ @param AtapiCommand The atapi command will be used for the transfer.
+ @param AtapiCommandLength The Length of the atapi command.
+ @param Read The transfer direction.
+ @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data.
+ @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data.
+ @param MemoryAddr The pointer to the Data Buffer.
+ @param DataCount The Data count to be transferred.
+ @param Timeout The timeout Value of non Data transfer.
+
+ @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval EFI_UNSUPPORTED The device is not ready for transfer.
+ @retval EFI_SUCCESS The PIO Data transfer executes successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciPioTransfer (
+ IN EFI_AHCI_REGISTERS *AhciRegisters,
+ IN UINT8 Port,
+ IN UINT8 PortMultiplier,
+ IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
+ IN UINT8 AtapiCommandLength,
+ IN BOOLEAN Read,
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
+ IN OUT VOID *MemoryAddr,
+ IN UINT32 DataCount,
+ IN UINT64 Timeout
+ );
+
+
+#endif
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c
new file mode 100644
index 0000000000..76204625dd
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.c
@@ -0,0 +1,767 @@
+/** @file
+ This driver is used for Opal Password Feature support at IDE mode.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "OpalPasswordSmm.h"
+
+/**
+ Write multiple words of Data to the IDE Data port.
+ Call the IO abstraction once to do the complete read,
+ not one word at a time
+
+ @param Port IO port to read
+ @param Count No. of UINT16's to read
+ @param Buffer Pointer to the Data Buffer for read
+
+**/
+VOID
+EFIAPI
+IdeWritePortWMultiple (
+ IN UINT16 Port,
+ IN UINTN Count,
+ IN UINT16 *Buffer
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Count; Index++) {
+ IoWrite16 (Port, Buffer[Index]);
+ }
+}
+
+/**
+ Reads multiple words of Data from the IDE Data port.
+ Call the IO abstraction once to do the complete read,
+ not one word at a time
+
+ @param Port IO port to read
+ @param Count Number of UINT16's to read
+ @param Buffer Pointer to the Data Buffer for read
+
+**/
+VOID
+EFIAPI
+IdeReadPortWMultiple (
+ IN UINT16 Port,
+ IN UINTN Count,
+ IN UINT16 *Buffer
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Count; Index++) {
+ Buffer[Count] = IoRead16 (Port);
+ }
+}
+
+/**
+ This function is used to analyze the Status Register and print out
+ some debug information and if there is ERR bit set in the Status
+ Register, the Error Register's Value is also be parsed and print out.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+
+**/
+VOID
+EFIAPI
+DumpAllIdeRegisters (
+ IN EFI_IDE_REGISTERS *IdeRegisters
+ )
+{
+ ASSERT (IdeRegisters != NULL);
+
+ DEBUG_CODE_BEGIN ();
+ if ((IoRead8 (IdeRegisters->CmdOrStatus) & ATA_STSREG_DWF) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Write Fault\n", IoRead8 (IdeRegisters->CmdOrStatus)));
+ }
+
+ if ((IoRead8 (IdeRegisters->CmdOrStatus) & ATA_STSREG_CORR) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Corrected Data\n", IoRead8 (IdeRegisters->CmdOrStatus)));
+ }
+
+ if ((IoRead8 (IdeRegisters->CmdOrStatus) & ATA_STSREG_ERR) != 0) {
+ if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_BBK) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Bad Block Detected\n", IoRead8 (IdeRegisters->ErrOrFeature)));
+ }
+
+ if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_UNC) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Uncorrectable Data\n", IoRead8 (IdeRegisters->ErrOrFeature)));
+ }
+
+ if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_MC) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Media Change\n", IoRead8 (IdeRegisters->ErrOrFeature)));
+ }
+
+ if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_ABRT) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Abort\n", IoRead8 (IdeRegisters->ErrOrFeature)));
+ }
+
+ if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_TK0NF) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Track 0 Not Found\n", IoRead8 (IdeRegisters->ErrOrFeature)));
+ }
+
+ if ((IoRead8 (IdeRegisters->ErrOrFeature) & ATA_ERRREG_AMNF) != 0) {
+ DEBUG ((EFI_D_ERROR, "CheckRegisterStatus()-- %02x : Error : Address Mark Not Found\n", IoRead8 (IdeRegisters->ErrOrFeature)));
+ }
+ }
+ DEBUG_CODE_END ();
+}
+
+/**
+ This function is used to analyze the Status Register and print out
+ some debug information and if there is ERR bit set in the Status
+ Register, the Error Register's Value is also be parsed and print out.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+
+ @retval EFI_SUCCESS No err information in the Status Register.
+ @retval EFI_DEVICE_ERROR Any err information in the Status Register.
+
+**/
+EFI_STATUS
+EFIAPI
+CheckStatusRegister (
+ IN EFI_IDE_REGISTERS *IdeRegisters
+ )
+{
+ EFI_STATUS Status;
+ UINT8 StatusRegister;
+
+ ASSERT (IdeRegisters != NULL);
+
+ StatusRegister = IoRead8 (IdeRegisters->CmdOrStatus);
+
+ if ((StatusRegister & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) {
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/**
+ This function is used to poll for the DRQ bit clear in the Status
+ Register. DRQ is cleared when the device is finished transferring Data.
+ So this function is called after Data transfer is finished.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS DRQ bit clear within the time out.
+ @retval EFI_TIMEOUT DRQ bit not clear within the time out.
+
+ @note
+ Read Status Register will clear interrupt status.
+
+**/
+EFI_STATUS
+EFIAPI
+DRQClear (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Delay;
+ UINT8 StatusRegister;
+
+ ASSERT (IdeRegisters != NULL);
+
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+ do {
+ StatusRegister = IoRead8 (IdeRegisters->CmdOrStatus);
+
+ //
+ // wait for BSY == 0 and DRQ == 0
+ //
+ if ((StatusRegister & ATA_STSREG_BSY) == 0) {
+
+ if ((StatusRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) {
+ return EFI_DEVICE_ERROR;
+ } else {
+ return EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay (100);
+
+ Delay--;
+
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+}
+/**
+ This function is used to poll for the DRQ bit clear in the Alternate
+ Status Register. DRQ is cleared when the device is finished
+ transferring Data. So this function is called after Data transfer
+ is finished.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS DRQ bit clear within the time out.
+
+ @retval EFI_TIMEOUT DRQ bit not clear within the time out.
+ @note Read Alternate Status Register will not clear interrupt status.
+
+**/
+EFI_STATUS
+EFIAPI
+DRQClear2 (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Delay;
+ UINT8 AltRegister;
+
+ ASSERT (IdeRegisters != NULL);
+
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+ do {
+ AltRegister = IoRead8 (IdeRegisters->AltOrDev);
+
+ //
+ // wait for BSY == 0 and DRQ == 0
+ //
+ if ((AltRegister & ATA_STSREG_BSY) == 0) {
+ if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) {
+ return EFI_DEVICE_ERROR;
+ } else {
+ return EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay (100);
+
+ Delay--;
+
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+}
+
+
+/**
+ This function is used to poll for the DRQ bit set in the Alternate Status Register.
+ DRQ is set when the device is ready to transfer Data. So this function is called after
+ the command is sent to the device and before required Data is transferred.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS DRQ bit set within the time out.
+ @retval EFI_TIMEOUT DRQ bit not set within the time out.
+ @retval EFI_ABORTED DRQ bit not set caused by the command abort.
+ @note Read Alternate Status Register will not clear interrupt status.
+
+**/
+EFI_STATUS
+EFIAPI
+DRQReady2 (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Delay;
+ UINT8 AltRegister;
+ UINT8 ErrorRegister;
+
+ ASSERT (IdeRegisters != NULL);
+
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+
+ do {
+ //
+ // Read Alternate Status Register will not clear interrupt status
+ //
+ AltRegister = IoRead8 (IdeRegisters->AltOrDev);
+ //
+ // BSY == 0 , DRQ == 1
+ //
+ if ((AltRegister & ATA_STSREG_BSY) == 0) {
+ if ((AltRegister & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
+ ErrorRegister = IoRead8 (IdeRegisters->ErrOrFeature);
+
+ if ((ErrorRegister & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
+ return EFI_ABORTED;
+ }
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((AltRegister & ATA_STSREG_DRQ) == ATA_STSREG_DRQ) {
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_READY;
+ }
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay (100);
+
+ Delay--;
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ This function is used to poll for the BSY bit clear in the Status Register. BSY
+ is clear when the device is not busy. Every command must be sent after device is not busy.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS BSY bit clear within the time out.
+ @retval EFI_TIMEOUT BSY bit not clear within the time out.
+
+ @note Read Status Register will clear interrupt status.
+**/
+EFI_STATUS
+EFIAPI
+WaitForBSYClear (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN UINT64 Timeout
+ )
+{
+ UINT32 Delay;
+ UINT8 StatusRegister;
+
+ ASSERT (IdeRegisters != NULL);
+
+ Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
+ do {
+ StatusRegister = IoRead8 (IdeRegisters->CmdOrStatus);
+
+ if ((StatusRegister & ATA_STSREG_BSY) == 0x00) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Stall for 100 microseconds.
+ //
+ MicroSecondDelay (100);
+
+ Delay--;
+
+ } while (Delay > 0);
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Get IDE i/o port registers' base addresses by mode.
+
+ In 'Compatibility' mode, use fixed addresses.
+ In Native-PCI mode, get base addresses from BARs in the PCI IDE controller's
+ Configuration Space.
+
+ The steps to get IDE i/o port registers' base addresses for each channel
+ as follows:
+
+ 1. Examine the Programming Interface byte of the Class Code fields in PCI IDE
+ controller's Configuration Space to determine the operating mode.
+
+ 2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below.
+ ___________________________________________
+ | | Command Block | Control Block |
+ | Channel | Registers | Registers |
+ |___________|_______________|_______________|
+ | Primary | 1F0h - 1F7h | 3F6h - 3F7h |
+ |___________|_______________|_______________|
+ | Secondary | 170h - 177h | 376h - 377h |
+ |___________|_______________|_______________|
+
+ Table 1. Compatibility resource mappings
+
+ b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs
+ in IDE controller's PCI Configuration Space, shown in the Table 2 below.
+ ___________________________________________________
+ | | Command Block | Control Block |
+ | Channel | Registers | Registers |
+ |___________|___________________|___________________|
+ | Primary | BAR at offset 0x10| BAR at offset 0x14|
+ |___________|___________________|___________________|
+ | Secondary | BAR at offset 0x18| BAR at offset 0x1C|
+ |___________|___________________|___________________|
+
+ Table 2. BARs for Register Mapping
+
+ @param[in] Bus The bus number of ata host controller.
+ @param[in] Device The device number of ata host controller.
+ @param[in] Function The function number of ata host controller.
+ @param[in, out] IdeRegisters Pointer to EFI_IDE_REGISTERS which is used to
+ store the IDE i/o port registers' base addresses
+
+ @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
+ @retval EFI_SUCCESS Get the Base address successfully
+ @retval Other Read the pci configureation Data error
+
+**/
+EFI_STATUS
+EFIAPI
+GetIdeRegisterIoAddr (
+ IN UINTN Bus,
+ IN UINTN Device,
+ IN UINTN Function,
+ IN OUT EFI_IDE_REGISTERS *IdeRegisters
+ )
+{
+ UINT16 CommandBlockBaseAddr;
+ UINT16 ControlBlockBaseAddr;
+ UINT8 ClassCode;
+ UINT32 BaseAddress[4];
+
+ if (IdeRegisters == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x9));
+ BaseAddress[0] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x10));
+ BaseAddress[1] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x14));
+ BaseAddress[2] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x18));
+ BaseAddress[3] = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x1C));
+
+ if ((ClassCode & IDE_PRIMARY_OPERATING_MODE) == 0) {
+ CommandBlockBaseAddr = 0x1f0;
+ ControlBlockBaseAddr = 0x3f6;
+ } else {
+ //
+ // The BARs should be of IO type
+ //
+ if ((BaseAddress[0] & BIT0) == 0 ||
+ (BaseAddress[1] & BIT0) == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CommandBlockBaseAddr = (UINT16) (BaseAddress[0] & 0x0000fff8);
+ ControlBlockBaseAddr = (UINT16) ((BaseAddress[1] & 0x0000fffc) + 2);
+ }
+
+ //
+ // Calculate IDE primary channel I/O register base address.
+ //
+ IdeRegisters[EfiIdePrimary].Data = CommandBlockBaseAddr;
+ IdeRegisters[EfiIdePrimary].ErrOrFeature = (UINT16) (CommandBlockBaseAddr + 0x01);
+ IdeRegisters[EfiIdePrimary].SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02);
+ IdeRegisters[EfiIdePrimary].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03);
+ IdeRegisters[EfiIdePrimary].CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04);
+ IdeRegisters[EfiIdePrimary].CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05);
+ IdeRegisters[EfiIdePrimary].Head = (UINT16) (CommandBlockBaseAddr + 0x06);
+ IdeRegisters[EfiIdePrimary].CmdOrStatus = (UINT16) (CommandBlockBaseAddr + 0x07);
+ IdeRegisters[EfiIdePrimary].AltOrDev = ControlBlockBaseAddr;
+
+ if ((ClassCode & IDE_SECONDARY_OPERATING_MODE) == 0) {
+ CommandBlockBaseAddr = 0x170;
+ ControlBlockBaseAddr = 0x376;
+ } else {
+ //
+ // The BARs should be of IO type
+ //
+ if ((BaseAddress[2] & BIT0) == 0 ||
+ (BaseAddress[3] & BIT0) == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CommandBlockBaseAddr = (UINT16) (BaseAddress[2] & 0x0000fff8);
+ ControlBlockBaseAddr = (UINT16) ((BaseAddress[3] & 0x0000fffc) + 2);
+ }
+
+ //
+ // Calculate IDE secondary channel I/O register base address.
+ //
+ IdeRegisters[EfiIdeSecondary].Data = CommandBlockBaseAddr;
+ IdeRegisters[EfiIdeSecondary].ErrOrFeature = (UINT16) (CommandBlockBaseAddr + 0x01);
+ IdeRegisters[EfiIdeSecondary].SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02);
+ IdeRegisters[EfiIdeSecondary].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03);
+ IdeRegisters[EfiIdeSecondary].CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04);
+ IdeRegisters[EfiIdeSecondary].CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05);
+ IdeRegisters[EfiIdeSecondary].Head = (UINT16) (CommandBlockBaseAddr + 0x06);
+ IdeRegisters[EfiIdeSecondary].CmdOrStatus = (UINT16) (CommandBlockBaseAddr + 0x07);
+ IdeRegisters[EfiIdeSecondary].AltOrDev = ControlBlockBaseAddr;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Send ATA Ext command into device with NON_DATA protocol.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS Reading succeed
+ @retval EFI_DEVICE_ERROR Error executing commands on this device.
+
+**/
+EFI_STATUS
+EFIAPI
+AtaIssueCommand (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
+ IN UINT64 Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINT8 DeviceHead;
+ UINT8 AtaCommand;
+
+ ASSERT (IdeRegisters != NULL);
+ ASSERT (AtaCommandBlock != NULL);
+
+ DeviceHead = AtaCommandBlock->AtaDeviceHead;
+ AtaCommand = AtaCommandBlock->AtaCommand;
+
+ Status = WaitForBSYClear (IdeRegisters, Timeout);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Select device (bit4), set LBA mode(bit6) (use 0xe0 for compatibility)
+ //
+ IoWrite8 (IdeRegisters->Head, (UINT8) (0xe0 | DeviceHead));
+
+ //
+ // set all the command parameters
+ // Before write to all the following registers, BSY and DRQ must be 0.
+ //
+ Status = DRQClear2 (IdeRegisters, Timeout);
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Fill the feature register, which is a two-byte FIFO. Need write twice.
+ //
+ IoWrite8 (IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeaturesExp);
+ IoWrite8 (IdeRegisters->ErrOrFeature, AtaCommandBlock->AtaFeatures);
+
+ //
+ // Fill the sector count register, which is a two-byte FIFO. Need write twice.
+ //
+ IoWrite8 (IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCountExp);
+ IoWrite8 (IdeRegisters->SectorCount, AtaCommandBlock->AtaSectorCount);
+
+ //
+ // Fill the start LBA registers, which are also two-byte FIFO
+ //
+ IoWrite8 (IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumberExp);
+ IoWrite8 (IdeRegisters->SectorNumber, AtaCommandBlock->AtaSectorNumber);
+
+ IoWrite8 (IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLowExp);
+ IoWrite8 (IdeRegisters->CylinderLsb, AtaCommandBlock->AtaCylinderLow);
+
+ IoWrite8 (IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHighExp);
+ IoWrite8 (IdeRegisters->CylinderMsb, AtaCommandBlock->AtaCylinderHigh);
+
+ //
+ // Send command via Command Register
+ //
+ IoWrite8 (IdeRegisters->CmdOrStatus, AtaCommand);
+
+ //
+ // Stall at least 400 microseconds.
+ //
+ MicroSecondDelay (400);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is used to send out ATA commands conforms to the PIO Data In Protocol.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Buffer A pointer to the source Buffer for the Data.
+ @param ByteCount The Length of the Data.
+ @param Read Flag used to determine the Data transfer direction.
+ Read equals 1, means Data transferred from device to host;
+ Read equals 0, means Data transferred from host to device.
+ @param AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK Data structure.
+ @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS send out the ATA command and device send required Data successfully.
+ @retval EFI_DEVICE_ERROR command sent failed.
+
+**/
+EFI_STATUS
+EFIAPI
+AtaPioDataInOut (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN OUT VOID *Buffer,
+ IN UINT64 ByteCount,
+ IN BOOLEAN Read,
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
+ IN UINT64 Timeout
+ )
+{
+ UINTN WordCount;
+ UINTN Increment;
+ UINT16 *Buffer16;
+ EFI_STATUS Status;
+
+ if ((IdeRegisters == NULL) || (Buffer == NULL) || (AtaCommandBlock == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Issue ATA command
+ //
+ Status = AtaIssueCommand (IdeRegisters, AtaCommandBlock, Timeout);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ Buffer16 = (UINT16 *) Buffer;
+
+ //
+ // According to PIO Data in protocol, host can perform a series of reads to
+ // the Data register after each time device set DRQ ready;
+ // The Data Size of "a series of read" is command specific.
+ // For most ATA command, Data Size received from device will not exceed
+ // 1 sector, hence the Data Size for "a series of read" can be the whole Data
+ // Size of one command request.
+ // For ATA command such as Read Sector command, the Data Size of one ATA
+ // command request is often larger than 1 sector, according to the
+ // Read Sector command, the Data Size of "a series of read" is exactly 1
+ // sector.
+ // Here for simplification reason, we specify the Data Size for
+ // "a series of read" to 1 sector (256 words) if Data Size of one ATA command
+ // request is larger than 256 words.
+ //
+ Increment = 256;
+
+ //
+ // used to record bytes of currently transfered Data
+ //
+ WordCount = 0;
+
+ while (WordCount < RShiftU64(ByteCount, 1)) {
+ //
+ // Poll DRQ bit set, Data transfer can be performed only when DRQ is ready
+ //
+ Status = DRQReady2 (IdeRegisters, Timeout);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ //
+ // Get the byte count for one series of read
+ //
+ if ((WordCount + Increment) > RShiftU64(ByteCount, 1)) {
+ Increment = (UINTN)(RShiftU64(ByteCount, 1) - WordCount);
+ }
+
+ if (Read) {
+ IdeReadPortWMultiple (
+ IdeRegisters->Data,
+ Increment,
+ Buffer16
+ );
+ } else {
+ IdeWritePortWMultiple (
+ IdeRegisters->Data,
+ Increment,
+ Buffer16
+ );
+ }
+
+ Status = CheckStatusRegister (IdeRegisters);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ WordCount += Increment;
+ Buffer16 += Increment;
+ }
+
+ Status = DRQClear (IdeRegisters, Timeout);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+Exit:
+ //
+ // Dump All Ide registers to ATA_STATUS_BLOCK
+ //
+ DumpAllIdeRegisters (IdeRegisters);
+
+ return Status;
+}
+
+/**
+ Sends out an ATA Identify Command to the specified device.
+
+ This function sends out the ATA Identify Command to the
+ specified device. Only ATA device responses to this command. If
+ the command succeeds, it returns the Identify Data structure which
+ contains information about the device. This function extracts the
+ information it needs to fill the IDE_BLK_IO_DEV Data structure,
+ including device type, media block Size, media capacity, and etc.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Channel The channel number of device.
+ @param Device The device number of device.
+ @param Buffer A pointer to Data Buffer which is used to contain IDENTIFY Data.
+
+ @retval EFI_SUCCESS Identify ATA device successfully.
+ @retval EFI_DEVICE_ERROR ATA Identify Device Command failed or device is not ATA device.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory failed.
+**/
+EFI_STATUS
+EFIAPI
+AtaIdentify (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN UINT8 Channel,
+ IN UINT8 Device,
+ IN OUT ATA_IDENTIFY_DATA *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
+
+ ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
+
+ AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;
+ AtaCommandBlock.AtaDeviceHead = (UINT8)(Device << 0x4);
+
+ Status = AtaPioDataInOut (
+ IdeRegisters,
+ Buffer,
+ sizeof (ATA_IDENTIFY_DATA),
+ TRUE,
+ &AtaCommandBlock,
+ NULL,
+ ATA_TIMEOUT
+ );
+
+ return Status;
+}
+
+
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h
new file mode 100644
index 0000000000..ba94a97505
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalIdeMode.h
@@ -0,0 +1,173 @@
+/** @file
+ Header file for IDE mode of ATA host controller.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+
+#ifndef __OPAL_PASSWORD_IDE_MODE_H__
+#define __OPAL_PASSWORD_IDE_MODE_H__
+
+typedef enum {
+ EfiIdePrimary = 0,
+ EfiIdeSecondary = 1,
+ EfiIdeMaxChannel = 2
+} EFI_IDE_CHANNEL;
+
+typedef enum {
+ EfiIdeMaster = 0,
+ EfiIdeSlave = 1,
+ EfiIdeMaxDevice = 2
+} EFI_IDE_DEVICE;
+
+//
+// IDE registers set
+//
+typedef struct {
+ UINT16 Data;
+ UINT16 ErrOrFeature;
+ UINT16 SectorCount;
+ UINT16 SectorNumber;
+ UINT16 CylinderLsb;
+ UINT16 CylinderMsb;
+ UINT16 Head;
+ UINT16 CmdOrStatus;
+ UINT16 AltOrDev;
+} EFI_IDE_REGISTERS;
+
+//
+// Bit definitions in Programming Interface byte of the Class Code field
+// in PCI IDE controller's Configuration Space
+//
+#define IDE_PRIMARY_OPERATING_MODE BIT0
+#define IDE_PRIMARY_PROGRAMMABLE_INDICATOR BIT1
+#define IDE_SECONDARY_OPERATING_MODE BIT2
+#define IDE_SECONDARY_PROGRAMMABLE_INDICATOR BIT3
+
+/**
+ Get IDE i/o port registers' base addresses by mode.
+
+ In 'Compatibility' mode, use fixed addresses.
+ In Native-PCI mode, get base addresses from BARs in the PCI IDE controller's
+ Configuration Space.
+
+ The steps to get IDE i/o port registers' base addresses for each channel
+ as follows:
+
+ 1. Examine the Programming Interface byte of the Class Code fields in PCI IDE
+ controller's Configuration Space to determine the operating mode.
+
+ 2. a) In 'Compatibility' mode, use fixed addresses shown in the Table 1 below.
+ ___________________________________________
+ | | Command Block | Control Block |
+ | Channel | Registers | Registers |
+ |___________|_______________|_______________|
+ | Primary | 1F0h - 1F7h | 3F6h - 3F7h |
+ |___________|_______________|_______________|
+ | Secondary | 170h - 177h | 376h - 377h |
+ |___________|_______________|_______________|
+
+ Table 1. Compatibility resource mappings
+
+ b) In Native-PCI mode, IDE registers are mapped into IO space using the BARs
+ in IDE controller's PCI Configuration Space, shown in the Table 2 below.
+ ___________________________________________________
+ | | Command Block | Control Block |
+ | Channel | Registers | Registers |
+ |___________|___________________|___________________|
+ | Primary | BAR at offset 0x10| BAR at offset 0x14|
+ |___________|___________________|___________________|
+ | Secondary | BAR at offset 0x18| BAR at offset 0x1C|
+ |___________|___________________|___________________|
+
+ Table 2. BARs for Register Mapping
+
+ @param[in] Bus The bus number of ata host controller.
+ @param[in] Device The device number of ata host controller.
+ @param[in] Function The function number of ata host controller.
+ @param[in, out] IdeRegisters Pointer to EFI_IDE_REGISTERS which is used to
+ store the IDE i/o port registers' base addresses
+
+ @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
+ @retval EFI_SUCCESS Get the Base address successfully
+ @retval Other Read the pci configureation Data error
+
+**/
+EFI_STATUS
+EFIAPI
+GetIdeRegisterIoAddr (
+ IN UINTN Bus,
+ IN UINTN Device,
+ IN UINTN Function,
+ IN OUT EFI_IDE_REGISTERS *IdeRegisters
+ );
+
+/**
+ Sends out an ATA Identify Command to the specified device.
+
+ This function sends out the ATA Identify Command to the
+ specified device. Only ATA device responses to this command. If
+ the command succeeds, it returns the Identify Data structure which
+ contains information about the device. This function extracts the
+ information it needs to fill the IDE_BLK_IO_DEV Data structure,
+ including device type, media block Size, media capacity, and etc.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Channel The channel number of device.
+ @param Device The device number of device.
+ @param Buffer A pointer to Data Buffer which is used to contain IDENTIFY Data.
+
+ @retval EFI_SUCCESS Identify ATA device successfully.
+ @retval EFI_DEVICE_ERROR ATA Identify Device Command failed or device is not ATA device.
+ @retval EFI_OUT_OF_RESOURCES Allocate memory failed.
+**/
+EFI_STATUS
+EFIAPI
+AtaIdentify (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN UINT8 Channel,
+ IN UINT8 Device,
+ IN OUT ATA_IDENTIFY_DATA *Buffer
+ );
+
+/**
+ This function is used to send out ATA commands conforms to the PIO Data In Protocol.
+
+ @param IdeRegisters A pointer to EFI_IDE_REGISTERS Data structure.
+ @param Buffer A pointer to the source Buffer for the Data.
+ @param ByteCount The Length of the Data.
+ @param Read Flag used to determine the Data transfer direction.
+ Read equals 1, means Data transferred from device to host;
+ Read equals 0, means Data transferred from host to device.
+ @param AtaCommandBlock A pointer to EFI_ATA_COMMAND_BLOCK Data structure.
+ @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK Data structure.
+ @param Timeout The time to complete the command.
+
+ @retval EFI_SUCCESS send out the ATA command and device send required Data successfully.
+ @retval EFI_DEVICE_ERROR command sent failed.
+
+**/
+EFI_STATUS
+EFIAPI
+AtaPioDataInOut (
+ IN EFI_IDE_REGISTERS *IdeRegisters,
+ IN OUT VOID *Buffer,
+ IN UINT64 ByteCount,
+ IN BOOLEAN Read,
+ IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
+ IN UINT64 Timeout
+ );
+
+
+#endif
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c
new file mode 100644
index 0000000000..ae9bfd9550
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.c
@@ -0,0 +1,2166 @@
+/** @file
+ Provide functions to initialize NVME controller and perform NVME commands
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "OpalPasswordSmm.h"
+
+
+#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a) - 1)) + 1)
+
+///
+/// NVME Host controller registers operation
+///
+#define NVME_GET_CAP(Nvme, Cap) NvmeMmioRead (Cap, Nvme->Nbar + NVME_CAP_OFFSET, sizeof (NVME_CAP))
+#define NVME_GET_CC(Nvme, Cc) NvmeMmioRead (Cc, Nvme->Nbar + NVME_CC_OFFSET, sizeof (NVME_CC))
+#define NVME_SET_CC(Nvme, Cc) NvmeMmioWrite (Nvme->Nbar + NVME_CC_OFFSET, Cc, sizeof (NVME_CC))
+#define NVME_GET_CSTS(Nvme, Csts) NvmeMmioRead (Csts, Nvme->Nbar + NVME_CSTS_OFFSET, sizeof (NVME_CSTS))
+#define NVME_GET_AQA(Nvme, Aqa) NvmeMmioRead (Aqa, Nvme->Nbar + NVME_AQA_OFFSET, sizeof (NVME_AQA))
+#define NVME_SET_AQA(Nvme, Aqa) NvmeMmioWrite (Nvme->Nbar + NVME_AQA_OFFSET, Aqa, sizeof (NVME_AQA))
+#define NVME_GET_ASQ(Nvme, Asq) NvmeMmioRead (Asq, Nvme->Nbar + NVME_ASQ_OFFSET, sizeof (NVME_ASQ))
+#define NVME_SET_ASQ(Nvme, Asq) NvmeMmioWrite (Nvme->Nbar + NVME_ASQ_OFFSET, Asq, sizeof (NVME_ASQ))
+#define NVME_GET_ACQ(Nvme, Acq) NvmeMmioRead (Acq, Nvme->Nbar + NVME_ACQ_OFFSET, sizeof (NVME_ACQ))
+#define NVME_SET_ACQ(Nvme, Acq) NvmeMmioWrite (Nvme->Nbar + NVME_ACQ_OFFSET, Acq, sizeof (NVME_ACQ))
+#define NVME_GET_VER(Nvme, Ver) NvmeMmioRead (Ver, Nvme->Nbar + NVME_VER_OFFSET, sizeof (NVME_VER))
+#define NVME_SET_SQTDBL(Nvme, Qid, Sqtdbl) NvmeMmioWrite (Nvme->Nbar + NVME_SQTDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Sqtdbl, sizeof (NVME_SQTDBL))
+#define NVME_SET_CQHDBL(Nvme, Qid, Cqhdbl) NvmeMmioWrite (Nvme->Nbar + NVME_CQHDBL_OFFSET(Qid, Nvme->Cap.Dstrd), Cqhdbl, sizeof (NVME_CQHDBL))
+
+///
+/// Base memory address
+///
+enum {
+ BASEMEM_CONTROLLER_DATA,
+ BASEMEM_IDENTIFY_DATA,
+ BASEMEM_ASQ,
+ BASEMEM_ACQ,
+ BASEMEM_SQ,
+ BASEMEM_CQ,
+ BASEMEM_PRP,
+ BASEMEM_SECURITY,
+ MAX_BASEMEM_COUNT
+};
+
+///
+/// All of base memories are 4K(0x1000) alignment
+///
+#define NVME_MEM_BASE(Nvme) (Nvme->BaseMem)
+#define NVME_CONTROL_DATA_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CONTROLLER_DATA)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_NAMESPACE_DATA_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_IDENTIFY_DATA)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_ASQ_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ASQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_ACQ_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_ACQ)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_SQ_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SQ) + ((index)*(NVME_MAX_IO_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_CQ_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_CQ) + ((index)*(NVME_MAX_IO_QUEUES-1))) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_PRP_BASE(Nvme, index) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_PRP) + ((index)*NVME_PRP_SIZE)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+#define NVME_SEC_BASE(Nvme) (ALIGN (NVME_MEM_BASE(Nvme) + ((NvmeGetBaseMemPages (BASEMEM_SECURITY)) * EFI_PAGE_SIZE), EFI_PAGE_SIZE))
+
+/**
+ Transfer MMIO Data to memory.
+
+ @param[in,out] MemBuffer - Destination: Memory address
+ @param[in] MmioAddr - Source: MMIO address
+ @param[in] Size - Size for read
+
+ @retval EFI_SUCCESS - MMIO read sucessfully
+**/
+EFI_STATUS
+NvmeMmioRead (
+ IN OUT VOID *MemBuffer,
+ IN UINTN MmioAddr,
+ IN UINTN Size
+ )
+{
+ UINTN Offset;
+ UINT8 Data;
+ UINT8 *Ptr;
+
+ // priority has adjusted
+ switch (Size) {
+ case 4:
+ *((UINT32 *)MemBuffer) = MmioRead32 (MmioAddr);
+ break;
+
+ case 8:
+ *((UINT64 *)MemBuffer) = MmioRead64 (MmioAddr);
+ break;
+
+ case 2:
+ *((UINT16 *)MemBuffer) = MmioRead16 (MmioAddr);
+ break;
+
+ case 1:
+ *((UINT8 *)MemBuffer) = MmioRead8 (MmioAddr);
+ break;
+
+ default:
+ Ptr = (UINT8 *)MemBuffer;
+ for (Offset = 0; Offset < Size; Offset += 1) {
+ Data = MmioRead8 (MmioAddr + Offset);
+ Ptr[Offset] = Data;
+ }
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Transfer memory data to MMIO.
+
+ @param[in,out] MmioAddr - Destination: MMIO address
+ @param[in] MemBuffer - Source: Memory address
+ @param[in] Size - Size for write
+
+ @retval EFI_SUCCESS - MMIO write sucessfully
+**/
+EFI_STATUS
+NvmeMmioWrite (
+ IN OUT UINTN MmioAddr,
+ IN VOID *MemBuffer,
+ IN UINTN Size
+ )
+{
+ UINTN Offset;
+ UINT8 Data;
+ UINT8 *Ptr;
+
+ // priority has adjusted
+ switch (Size) {
+ case 4:
+ MmioWrite32 (MmioAddr, *((UINT32 *)MemBuffer));
+ break;
+
+ case 8:
+ MmioWrite64 (MmioAddr, *((UINT64 *)MemBuffer));
+ break;
+
+ case 2:
+ MmioWrite16 (MmioAddr, *((UINT16 *)MemBuffer));
+ break;
+
+ case 1:
+ MmioWrite8 (MmioAddr, *((UINT8 *)MemBuffer));
+ break;
+
+ default:
+ Ptr = (UINT8 *)MemBuffer;
+ for (Offset = 0; Offset < Size; Offset += 1) {
+ Data = Ptr[Offset];
+ MmioWrite8 (MmioAddr + Offset, Data);
+ }
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Transfer MMIO data to memory.
+
+ @param[in,out] MemBuffer - Destination: Memory address
+ @param[in] MmioAddr - Source: MMIO address
+ @param[in] Size - Size for read
+
+ @retval EFI_SUCCESS - MMIO read sucessfully
+**/
+EFI_STATUS
+OpalPciRead (
+ IN OUT VOID *MemBuffer,
+ IN UINTN MmioAddr,
+ IN UINTN Size
+ )
+{
+ UINTN Offset;
+ UINT8 Data;
+ UINT8 *Ptr;
+
+ // priority has adjusted
+ switch (Size) {
+ case 4:
+ *((UINT32 *)MemBuffer) = PciRead32 (MmioAddr);
+ break;
+
+ case 2:
+ *((UINT16 *)MemBuffer) = PciRead16 (MmioAddr);
+ break;
+
+ case 1:
+ *((UINT8 *)MemBuffer) = PciRead8 (MmioAddr);
+ break;
+
+ default:
+ Ptr = (UINT8 *)MemBuffer;
+ for (Offset = 0; Offset < Size; Offset += 1) {
+ Data = PciRead8 (MmioAddr + Offset);
+ Ptr[Offset] = Data;
+ }
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Transfer memory data to MMIO.
+
+ @param[in,out] MmioAddr - Destination: MMIO address
+ @param[in] MemBuffer - Source: Memory address
+ @param[in] Size - Size for write
+
+ @retval EFI_SUCCESS - MMIO write sucessfully
+**/
+EFI_STATUS
+OpalPciWrite (
+ IN OUT UINTN MmioAddr,
+ IN VOID *MemBuffer,
+ IN UINTN Size
+ )
+{
+ UINTN Offset;
+ UINT8 Data;
+ UINT8 *Ptr;
+
+ // priority has adjusted
+ switch (Size) {
+ case 4:
+ PciWrite32 (MmioAddr, *((UINT32 *)MemBuffer));
+ break;
+
+ case 2:
+ PciWrite16 (MmioAddr, *((UINT16 *)MemBuffer));
+ break;
+
+ case 1:
+ PciWrite8 (MmioAddr, *((UINT8 *)MemBuffer));
+ break;
+
+ default:
+ Ptr = (UINT8 *)MemBuffer;
+ for (Offset = 0; Offset < Size; Offset += 1) {
+ Data = Ptr[Offset];
+ PciWrite8 (MmioAddr + Offset, Data);
+ }
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get total pages for specific NVME based memory.
+
+ @param[in] BaseMemIndex - The Index of BaseMem (0-based).
+
+ @retval - The page count for specific BaseMem Index
+
+**/
+UINT32
+NvmeGetBaseMemPages (
+ IN UINTN BaseMemIndex
+ )
+{
+ UINT32 Pages;
+ UINTN Index;
+ UINT32 PageSizeList[8];
+
+ PageSizeList[0] = 1; /* Controller Data */
+ PageSizeList[1] = 1; /* Identify Data */
+ PageSizeList[2] = 1; /* ASQ */
+ PageSizeList[3] = 1; /* ACQ */
+ PageSizeList[4] = 1; /* SQs */
+ PageSizeList[5] = 1; /* CQs */
+ PageSizeList[6] = NVME_PRP_SIZE * NVME_CSQ_DEPTH; /* PRPs */
+ PageSizeList[7] = 1; /* Security Commands */
+
+ if (BaseMemIndex > MAX_BASEMEM_COUNT) {
+ ASSERT (FALSE);
+ return 0;
+ }
+
+ Pages = 0;
+ for (Index = 0; Index < BaseMemIndex; Index++) {
+ Pages += PageSizeList[Index];
+ }
+
+ return Pages;
+}
+
+/**
+ Wait for NVME controller status to be ready or not.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] WaitReady - Flag for waitting status ready or not
+
+ @return EFI_SUCCESS - Successfully to wait specific status.
+ @return others - Fail to wait for specific controller status.
+
+**/
+STATIC
+EFI_STATUS
+NvmeWaitController (
+ IN NVME_CONTEXT *Nvme,
+ IN BOOLEAN WaitReady
+ )
+{
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT8 Timeout;
+
+ //
+ // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after
+ // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.
+ //
+ if (Nvme->Cap.To == 0) {
+ Timeout = 1;
+ } else {
+ Timeout = Nvme->Cap.To;
+ }
+
+ Status = EFI_SUCCESS;
+ for(Index = (Timeout * 500); Index != 0; --Index) {
+ MicroSecondDelay (1000);
+
+ //
+ // Check if the controller is initialized
+ //
+ Status = NVME_GET_CSTS (Nvme, &Csts);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status));
+ return Status;
+ }
+
+ if ((BOOLEAN) Csts.Rdy == WaitReady) {
+ break;
+ }
+ }
+
+ if (Index == 0) {
+ Status = EFI_TIMEOUT;
+ }
+
+ return Status;
+}
+
+/**
+ Disable the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully disable the controller.
+ @return others - Fail to disable the controller.
+
+**/
+STATIC
+EFI_STATUS
+NvmeDisableController (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVME_CC Cc;
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+
+ Status = NVME_GET_CSTS (Nvme, &Csts);
+
+ ///
+ /// Read Controller Configuration Register.
+ ///
+ Status = NVME_GET_CC (Nvme, &Cc);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status));
+ goto Done;
+ }
+
+ if (Cc.En == 1) {
+ Cc.En = 0;
+ ///
+ /// Disable the controller.
+ ///
+ Status = NVME_SET_CC (Nvme, &Cc);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));
+ goto Done;
+ }
+ }
+
+ Status = NvmeWaitController (Nvme, FALSE);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status));
+ goto Done;
+ }
+
+ return EFI_SUCCESS;
+
+Done:
+ DEBUG ((DEBUG_INFO, "NvmeDisableController fail, Status: %r\n", Status));
+ return Status;
+}
+
+/**
+ Enable the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully enable the controller.
+ @return EFI_DEVICE_ERROR - Fail to enable the controller.
+ @return EFI_TIMEOUT - Fail to enable the controller in given time slot.
+
+**/
+STATIC
+EFI_STATUS
+NvmeEnableController (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVME_CC Cc;
+ EFI_STATUS Status;
+
+ //
+ // Enable the controller
+ //
+ ZeroMem (&Cc, sizeof (NVME_CC));
+ Cc.En = 1;
+ Cc.Iosqes = 6;
+ Cc.Iocqes = 4;
+ Status = NVME_SET_CC (Nvme, &Cc);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));
+ goto Done;
+ }
+
+ Status = NvmeWaitController (Nvme, TRUE);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeWaitController fail, Status = %r\n", Status));
+ goto Done;
+ }
+
+ return EFI_SUCCESS;
+
+Done:
+ DEBUG ((DEBUG_INFO, "NvmeEnableController fail, Status: %r\n", Status));
+ return Status;
+}
+
+/**
+ Shutdown the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully shutdown the controller.
+ @return EFI_DEVICE_ERROR - Fail to shutdown the controller.
+ @return EFI_TIMEOUT - Fail to shutdown the controller in given time slot.
+
+**/
+STATIC
+EFI_STATUS
+NvmeShutdownController (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVME_CC Cc;
+ NVME_CSTS Csts;
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINTN Timeout;
+
+ Status = NVME_GET_CC (Nvme, &Cc);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CC fail, Status = %r\n", Status));
+ return Status;
+ }
+
+ Cc.Shn = 1; // Normal shutdown
+
+ Status = NVME_SET_CC (Nvme, &Cc);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_SET_CC fail, Status = %r\n", Status));
+ return Status;
+ }
+
+ Timeout = NVME_GENERIC_TIMEOUT/1000; // ms
+ for(Index = (UINT32)(Timeout); Index != 0; --Index) {
+ MicroSecondDelay (1000);
+
+ Status = NVME_GET_CSTS (Nvme, &Csts);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CSTS fail, Status = %r\n", Status));
+ return Status;
+ }
+
+ if (Csts.Shst == 2) { // Shutdown processing complete
+ break;
+ }
+ }
+
+ if (Index == 0) {
+ Status = EFI_TIMEOUT;
+ }
+
+ return Status;
+}
+
+/**
+ Check the execution status from a given completion queue entry.
+
+ @param[in] Cq - A pointer to the NVME_CQ item.
+
+**/
+EFI_STATUS
+NvmeCheckCqStatus (
+ IN NVME_CQ *Cq
+ )
+{
+ if (Cq->Sct == 0x0 && Cq->Sc == 0x0) {
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_INFO, "Dump NVMe Completion Entry Status from [0x%x]:\n", (UINTN)Cq));
+ DEBUG ((DEBUG_INFO, " SQ Identifier : [0x%x], Phase Tag : [%d], Cmd Identifier : [0x%x]\n", Cq->Sqid, Cq->Pt, Cq->Cid));
+ DEBUG ((DEBUG_INFO, " NVMe Cmd Execution Result - "));
+
+ switch (Cq->Sct) {
+ case 0x0:
+ switch (Cq->Sc) {
+ case 0x0:
+ DEBUG ((DEBUG_INFO, "Successful Completion\n"));
+ return EFI_SUCCESS;
+ case 0x1:
+ DEBUG ((DEBUG_INFO, "Invalid Command Opcode\n"));
+ break;
+ case 0x2:
+ DEBUG ((DEBUG_INFO, "Invalid Field in Command\n"));
+ break;
+ case 0x3:
+ DEBUG ((DEBUG_INFO, "Command ID Conflict\n"));
+ break;
+ case 0x4:
+ DEBUG ((DEBUG_INFO, "Data Transfer Error\n"));
+ break;
+ case 0x5:
+ DEBUG ((DEBUG_INFO, "Commands Aborted due to Power Loss Notification\n"));
+ break;
+ case 0x6:
+ DEBUG ((DEBUG_INFO, "Internal Device Error\n"));
+ break;
+ case 0x7:
+ DEBUG ((DEBUG_INFO, "Command Abort Requested\n"));
+ break;
+ case 0x8:
+ DEBUG ((DEBUG_INFO, "Command Aborted due to SQ Deletion\n"));
+ break;
+ case 0x9:
+ DEBUG ((DEBUG_INFO, "Command Aborted due to Failed Fused Command\n"));
+ break;
+ case 0xA:
+ DEBUG ((DEBUG_INFO, "Command Aborted due to Missing Fused Command\n"));
+ break;
+ case 0xB:
+ DEBUG ((DEBUG_INFO, "Invalid Namespace or Format\n"));
+ break;
+ case 0xC:
+ DEBUG ((DEBUG_INFO, "Command Sequence Error\n"));
+ break;
+ case 0xD:
+ DEBUG ((DEBUG_INFO, "Invalid SGL Last Segment Descriptor\n"));
+ break;
+ case 0xE:
+ DEBUG ((DEBUG_INFO, "Invalid Number of SGL Descriptors\n"));
+ break;
+ case 0xF:
+ DEBUG ((DEBUG_INFO, "Data SGL Length Invalid\n"));
+ break;
+ case 0x10:
+ DEBUG ((DEBUG_INFO, "Metadata SGL Length Invalid\n"));
+ break;
+ case 0x11:
+ DEBUG ((DEBUG_INFO, "SGL Descriptor Type Invalid\n"));
+ break;
+ case 0x80:
+ DEBUG ((DEBUG_INFO, "LBA Out of Range\n"));
+ break;
+ case 0x81:
+ DEBUG ((DEBUG_INFO, "Capacity Exceeded\n"));
+ break;
+ case 0x82:
+ DEBUG ((DEBUG_INFO, "Namespace Not Ready\n"));
+ break;
+ case 0x83:
+ DEBUG ((DEBUG_INFO, "Reservation Conflict\n"));
+ break;
+ }
+ break;
+
+ case 0x1:
+ switch (Cq->Sc) {
+ case 0x0:
+ DEBUG ((DEBUG_INFO, "Completion Queue Invalid\n"));
+ break;
+ case 0x1:
+ DEBUG ((DEBUG_INFO, "Invalid Queue Identifier\n"));
+ break;
+ case 0x2:
+ DEBUG ((DEBUG_INFO, "Maximum Queue Size Exceeded\n"));
+ break;
+ case 0x3:
+ DEBUG ((DEBUG_INFO, "Abort Command Limit Exceeded\n"));
+ break;
+ case 0x5:
+ DEBUG ((DEBUG_INFO, "Asynchronous Event Request Limit Exceeded\n"));
+ break;
+ case 0x6:
+ DEBUG ((DEBUG_INFO, "Invalid Firmware Slot\n"));
+ break;
+ case 0x7:
+ DEBUG ((DEBUG_INFO, "Invalid Firmware Image\n"));
+ break;
+ case 0x8:
+ DEBUG ((DEBUG_INFO, "Invalid Interrupt Vector\n"));
+ break;
+ case 0x9:
+ DEBUG ((DEBUG_INFO, "Invalid Log Page\n"));
+ break;
+ case 0xA:
+ DEBUG ((DEBUG_INFO, "Invalid Format\n"));
+ break;
+ case 0xB:
+ DEBUG ((DEBUG_INFO, "Firmware Application Requires Conventional Reset\n"));
+ break;
+ case 0xC:
+ DEBUG ((DEBUG_INFO, "Invalid Queue Deletion\n"));
+ break;
+ case 0xD:
+ DEBUG ((DEBUG_INFO, "Feature Identifier Not Saveable\n"));
+ break;
+ case 0xE:
+ DEBUG ((DEBUG_INFO, "Feature Not Changeable\n"));
+ break;
+ case 0xF:
+ DEBUG ((DEBUG_INFO, "Feature Not Namespace Specific\n"));
+ break;
+ case 0x10:
+ DEBUG ((DEBUG_INFO, "Firmware Application Requires NVM Subsystem Reset\n"));
+ break;
+ case 0x80:
+ DEBUG ((DEBUG_INFO, "Conflicting Attributes\n"));
+ break;
+ case 0x81:
+ DEBUG ((DEBUG_INFO, "Invalid Protection Information\n"));
+ break;
+ case 0x82:
+ DEBUG ((DEBUG_INFO, "Attempted Write to Read Only Range\n"));
+ break;
+ }
+ break;
+
+ case 0x2:
+ switch (Cq->Sc) {
+ case 0x80:
+ DEBUG ((DEBUG_INFO, "Write Fault\n"));
+ break;
+ case 0x81:
+ DEBUG ((DEBUG_INFO, "Unrecovered Read Error\n"));
+ break;
+ case 0x82:
+ DEBUG ((DEBUG_INFO, "End-to-end Guard Check Error\n"));
+ break;
+ case 0x83:
+ DEBUG ((DEBUG_INFO, "End-to-end Application Tag Check Error\n"));
+ break;
+ case 0x84:
+ DEBUG ((DEBUG_INFO, "End-to-end Reference Tag Check Error\n"));
+ break;
+ case 0x85:
+ DEBUG ((DEBUG_INFO, "Compare Failure\n"));
+ break;
+ case 0x86:
+ DEBUG ((DEBUG_INFO, "Access Denied\n"));
+ break;
+ }
+ break;
+
+ default:
+ DEBUG ((DEBUG_INFO, "Unknown error\n"));
+ break;
+ }
+
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Create PRP lists for Data transfer which is larger than 2 memory pages.
+ Note here we calcuate the number of required PRP lists and allocate them at one time.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] SqId - The SQ index for this PRP
+ @param[in] PhysicalAddr - The physical base address of Data Buffer.
+ @param[in] Pages - The number of pages to be transfered.
+ @param[out] PrpListHost - The host base address of PRP lists.
+ @param[in,out] PrpListNo - The number of PRP List.
+
+ @retval The pointer Value to the first PRP List of the PRP lists.
+
+**/
+STATIC
+UINT64
+NvmeCreatePrpList (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT16 SqId,
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddr,
+ IN UINTN Pages,
+ OUT VOID **PrpListHost,
+ IN OUT UINTN *PrpListNo
+ )
+{
+ UINTN PrpEntryNo;
+ UINT64 PrpListBase;
+ UINTN PrpListIndex;
+ UINTN PrpEntryIndex;
+ UINT64 Remainder;
+ EFI_PHYSICAL_ADDRESS PrpListPhyAddr;
+ UINTN Bytes;
+ UINT8 *PrpEntry;
+ EFI_PHYSICAL_ADDRESS NewPhyAddr;
+
+ ///
+ /// The number of Prp Entry in a memory page.
+ ///
+ PrpEntryNo = EFI_PAGE_SIZE / sizeof (UINT64);
+
+ ///
+ /// Calculate total PrpList number.
+ ///
+ *PrpListNo = (UINTN) DivU64x64Remainder ((UINT64)Pages, (UINT64)PrpEntryNo, &Remainder);
+ if (Remainder != 0) {
+ *PrpListNo += 1;
+ }
+
+ if (*PrpListNo > NVME_PRP_SIZE) {
+ DEBUG ((DEBUG_INFO, "NvmeCreatePrpList (PhysicalAddr: %lx, Pages: %x) PrpEntryNo: %x\n",
+ PhysicalAddr, Pages, PrpEntryNo));
+ DEBUG ((DEBUG_INFO, "*PrpListNo: %x, Remainder: %lx", *PrpListNo, Remainder));
+ ASSERT (FALSE);
+ }
+ *PrpListHost = (VOID *)(UINTN) NVME_PRP_BASE (Nvme, SqId);
+
+ Bytes = EFI_PAGES_TO_SIZE (*PrpListNo);
+ PrpListPhyAddr = (UINT64)(UINTN)(*PrpListHost);
+
+ ///
+ /// Fill all PRP lists except of last one.
+ ///
+ ZeroMem (*PrpListHost, Bytes);
+ for (PrpListIndex = 0; PrpListIndex < *PrpListNo - 1; ++PrpListIndex) {
+ PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
+
+ for (PrpEntryIndex = 0; PrpEntryIndex < PrpEntryNo; ++PrpEntryIndex) {
+ PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));
+ if (PrpEntryIndex != PrpEntryNo - 1) {
+ ///
+ /// Fill all PRP entries except of last one.
+ ///
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));
+ PhysicalAddr += EFI_PAGE_SIZE;
+ } else {
+ ///
+ /// Fill last PRP entries with next PRP List pointer.
+ ///
+ NewPhyAddr = (PrpListPhyAddr + (PrpListIndex + 1) * EFI_PAGE_SIZE);
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&NewPhyAddr), sizeof (UINT64));
+ }
+ }
+ }
+
+ ///
+ /// Fill last PRP list.
+ ///
+ PrpListBase = *(UINT64*)PrpListHost + PrpListIndex * EFI_PAGE_SIZE;
+ for (PrpEntryIndex = 0; PrpEntryIndex < ((Remainder != 0) ? Remainder : PrpEntryNo); ++PrpEntryIndex) {
+ PrpEntry = (UINT8 *)(UINTN) (PrpListBase + PrpEntryIndex * sizeof(UINT64));
+ CopyMem (PrpEntry, (VOID *)(UINTN) (&PhysicalAddr), sizeof (UINT64));
+
+ PhysicalAddr += EFI_PAGE_SIZE;
+ }
+
+ return PrpListPhyAddr;
+}
+
+/**
+ Check whether there are available command slots.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Qid - Queue index
+
+ @retval EFI_SUCCESS - Available command slot is found
+ @retval EFI_NOT_READY - No available command slot is found
+ @retval EFI_DEVICE_ERROR - Error occurred on device side.
+
+**/
+EFI_STATUS
+NvmeHasFreeCmdSlot (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT8 Qid
+ )
+{
+ return TRUE;
+}
+
+/**
+ Check whether all command slots are clean.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Qid - Queue index
+
+ @retval EFI_SUCCESS - All command slots are clean
+ @retval EFI_NOT_READY - Not all command slots are clean
+ @retval EFI_DEVICE_ERROR - Error occurred on device side.
+
+**/
+EFI_STATUS
+NvmeIsAllCmdSlotClean (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT8 Qid
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ Waits until all NVME commands completed.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Qid - Queue index
+
+ @retval EFI_SUCCESS - All NVME commands have completed
+ @retval EFI_TIMEOUT - Timeout occured
+ @retval EFI_NOT_READY - Not all NVME commands have completed
+ @retval others - Error occurred on device side.
+**/
+EFI_STATUS
+NvmeWaitAllComplete (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT8 Qid
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
+ both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking
+ I/O functionality is optional.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] NamespaceId - Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace
+ ID specifies that the command packet should be sent to all valid namespaces.
+ @param[in] NamespaceUuid - Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace
+ UUID specifies that the command packet should be sent to all valid namespaces.
+ @param[in,out] Packet - A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified
+ by NamespaceId.
+
+ @retval EFI_SUCCESS - The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred
+ to, or from DataBuffer.
+ @retval EFI_NOT_READY - The NVM Express Command Packet could not be sent because the controller is not ready. The caller
+ may retry again later.
+ @retval EFI_DEVICE_ERROR - A device error occurred while attempting to send the NVM Express Command Packet.
+ @retval EFI_INVALID_PARAMETER - Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM
+ Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_UNSUPPORTED - The command described by the NVM Express Command Packet is not supported by the host adapter.
+ The NVM Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT - A timeout occurred while waiting for the NVM Express Command Packet to execute.
+
+**/
+EFI_STATUS
+NvmePassThru (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ NVME_SQ *Sq;
+ NVME_CQ *Cq;
+ UINT8 Qid;
+ UINT32 Bytes;
+ UINT32 Offset;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *PrpListHost;
+ UINTN PrpListNo;
+ UINT32 Timer;
+ UINTN SqSize;
+ UINTN CqSize;
+
+ ///
+ /// check the Data fields in Packet parameter.
+ ///
+ if ((Nvme == NULL) || (Packet == NULL)) {
+ DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: Nvme(%x)/Packet(%x)\n",
+ (UINTN)Nvme, (UINTN)Packet));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet->NvmeCmd == NULL) || (Packet->NvmeResponse == NULL)) {
+ DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: NvmeCmd(%x)/NvmeResponse(%x)\n",
+ (UINTN)Packet->NvmeCmd, (UINTN)Packet->NvmeResponse));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet->QueueId != NVME_ADMIN_QUEUE && Packet->QueueId != NVME_IO_QUEUE) {
+ DEBUG ((DEBUG_ERROR, "NvmePassThru, invalid parameter: QueueId(%x)\n",
+ Packet->QueueId));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PrpListHost = NULL;
+ PrpListNo = 0;
+ Status = EFI_SUCCESS;
+
+ Qid = Packet->QueueId;
+ Sq = Nvme->SqBuffer[Qid] + Nvme->SqTdbl[Qid].Sqt;
+ Cq = Nvme->CqBuffer[Qid] + Nvme->CqHdbl[Qid].Cqh;
+ if (Qid == NVME_ADMIN_QUEUE) {
+ SqSize = NVME_ASQ_SIZE + 1;
+ CqSize = NVME_ACQ_SIZE + 1;
+ } else {
+ SqSize = NVME_CSQ_DEPTH;
+ CqSize = NVME_CCQ_DEPTH;
+ }
+
+ if (Packet->NvmeCmd->Nsid != NamespaceId) {
+ DEBUG ((DEBUG_ERROR, "NvmePassThru: Nsid mismatch (%x, %x)\n",
+ Packet->NvmeCmd->Nsid, NamespaceId));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (Sq, sizeof (NVME_SQ));
+ Sq->Opc = Packet->NvmeCmd->Cdw0.Opcode;
+ Sq->Fuse = Packet->NvmeCmd->Cdw0.FusedOperation;
+ Sq->Cid = Packet->NvmeCmd->Cdw0.Cid;
+ Sq->Nsid = Packet->NvmeCmd->Nsid;
+
+ ///
+ /// Currently we only support PRP for Data transfer, SGL is NOT supported.
+ ///
+ ASSERT (Sq->Psdt == 0);
+ if (Sq->Psdt != 0) {
+ DEBUG ((DEBUG_ERROR, "NvmePassThru: doesn't support SGL mechanism\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ Sq->Prp[0] = Packet->TransferBuffer;
+ Sq->Prp[1] = 0;
+
+ if(Packet->MetadataBuffer != (UINT64)(UINTN)NULL) {
+ Sq->Mptr = Packet->MetadataBuffer;
+ }
+
+ ///
+ /// If the Buffer Size spans more than two memory pages (page Size as defined in CC.Mps),
+ /// then build a PRP list in the second PRP submission queue entry.
+ ///
+ Offset = ((UINT32)Sq->Prp[0]) & (EFI_PAGE_SIZE - 1);
+ Bytes = Packet->TransferLength;
+
+ if ((Offset + Bytes) > (EFI_PAGE_SIZE * 2)) {
+ ///
+ /// Create PrpList for remaining Data Buffer.
+ ///
+ PhyAddr = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);
+ Sq->Prp[1] = NvmeCreatePrpList (Nvme, Nvme->SqTdbl[Qid].Sqt, PhyAddr, EFI_SIZE_TO_PAGES(Offset + Bytes) - 1, &PrpListHost, &PrpListNo);
+ if (Sq->Prp[1] == 0) {
+ Status = EFI_OUT_OF_RESOURCES;
+ DEBUG ((DEBUG_ERROR, "NvmeCreatePrpList fail, Status: %r\n", Status));
+ goto EXIT;
+ }
+
+ } else if ((Offset + Bytes) > EFI_PAGE_SIZE) {
+ Sq->Prp[1] = (Sq->Prp[0] + EFI_PAGE_SIZE) & ~(EFI_PAGE_SIZE - 1);
+ }
+
+ if(Packet->NvmeCmd->Flags & CDW10_VALID) {
+ Sq->Payload.Raw.Cdw10 = Packet->NvmeCmd->Cdw10;
+ }
+ if(Packet->NvmeCmd->Flags & CDW11_VALID) {
+ Sq->Payload.Raw.Cdw11 = Packet->NvmeCmd->Cdw11;
+ }
+ if(Packet->NvmeCmd->Flags & CDW12_VALID) {
+ Sq->Payload.Raw.Cdw12 = Packet->NvmeCmd->Cdw12;
+ }
+ if(Packet->NvmeCmd->Flags & CDW13_VALID) {
+ Sq->Payload.Raw.Cdw13 = Packet->NvmeCmd->Cdw13;
+ }
+ if(Packet->NvmeCmd->Flags & CDW14_VALID) {
+ Sq->Payload.Raw.Cdw14 = Packet->NvmeCmd->Cdw14;
+ }
+ if(Packet->NvmeCmd->Flags & CDW15_VALID) {
+ Sq->Payload.Raw.Cdw15 = Packet->NvmeCmd->Cdw15;
+ }
+
+#if (EN_NVME_VERBOSE_DBINFO == ON)
+ //DumpMem (Sq, sizeof (NVME_SQ));
+#endif
+ ///
+ /// Ring the submission queue doorbell.
+ ///
+ Nvme->SqTdbl[Qid].Sqt++;
+ if(Nvme->SqTdbl[Qid].Sqt == SqSize) {
+ Nvme->SqTdbl[Qid].Sqt = 0;
+ }
+ Status = NVME_SET_SQTDBL (Nvme, Qid, &Nvme->SqTdbl[Qid]);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_SET_SQTDBL fail, Status: %r\n", Status));
+ goto EXIT;
+ }
+
+ ///
+ /// Wait for completion queue to get filled in.
+ ///
+ Status = EFI_TIMEOUT;
+ Timer = 0;
+ while (Timer < NVME_CMD_TIMEOUT) {
+ //DEBUG ((DEBUG_VERBOSE, "Timer: %x, Cq:\n", Timer));
+ //DumpMem (Cq, sizeof (NVME_CQ));
+ if (Cq->Pt != Nvme->Pt[Qid]) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+
+ MicroSecondDelay (NVME_CMD_WAIT);
+ Timer += NVME_CMD_WAIT;
+ }
+
+ Nvme->CqHdbl[Qid].Cqh++;
+ if (Nvme->CqHdbl[Qid].Cqh == CqSize) {
+ Nvme->CqHdbl[Qid].Cqh = 0;
+ Nvme->Pt[Qid] ^= 1;
+ }
+
+ ///
+ /// Copy the Respose Queue entry for this command to the callers response Buffer
+ ///
+ CopyMem (Packet->NvmeResponse, Cq, sizeof(NVM_EXPRESS_RESPONSE));
+
+ if (!EFI_ERROR(Status)) { // We still need to check CQ status if no timeout error occured
+ Status = NvmeCheckCqStatus (Cq);
+ }
+ NVME_SET_CQHDBL (Nvme, Qid, &Nvme->CqHdbl[Qid]);
+
+EXIT:
+ return Status;
+}
+
+/**
+ Get identify controller Data.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Buffer - The Buffer used to store the identify controller Data.
+
+ @return EFI_SUCCESS - Successfully get the identify controller Data.
+ @return others - Fail to get the identify controller Data.
+
+**/
+STATIC
+EFI_STATUS
+NvmeIdentifyController (
+ IN NVME_CONTEXT *Nvme,
+ IN VOID *Buffer
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ //
+ // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
+ // For the Identify command, the Namespace Identifier is only used for the Namespace Data structure.
+ //
+ Command.Nsid = 0;
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer;
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify a controller
+ //
+ Command.Cdw10 = 1;
+ Command.Flags = CDW10_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Get specified identify namespace Data.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] NamespaceId - The specified namespace identifier.
+ @param[in] Buffer - The Buffer used to store the identify namespace Data.
+
+ @return EFI_SUCCESS - Successfully get the identify namespace Data.
+ @return others - Fail to get the identify namespace Data.
+
+**/
+STATIC
+EFI_STATUS
+NvmeIdentifyNamespace (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT32 NamespaceId,
+ IN VOID *Buffer
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_OPC;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ Command.Nsid = NamespaceId;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Buffer;
+ CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify a namespace
+ //
+ CommandPacket.NvmeCmd->Cdw10 = 0;
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NamespaceId,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Get Block Size for specific namespace of NVME.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return - Block Size in bytes
+
+**/
+STATIC
+UINT32
+NvmeGetBlockSize (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ UINT32 BlockSize;
+ UINT32 Lbads;
+ UINT32 Flbas;
+ UINT32 LbaFmtIdx;
+
+ Flbas = Nvme->NamespaceData->Flbas;
+ LbaFmtIdx = Flbas & 3;
+ Lbads = Nvme->NamespaceData->LbaFormat[LbaFmtIdx].Lbads;
+
+ BlockSize = (UINT32)1 << Lbads;
+ return BlockSize;
+}
+
+/**
+ Get last LBA for specific namespace of NVME.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return - Last LBA address
+
+**/
+STATIC
+EFI_LBA
+NvmeGetLastLba (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ EFI_LBA LastBlock;
+ LastBlock = Nvme->NamespaceData->Nsze - 1;
+ return LastBlock;
+}
+
+/**
+ Create io completion queue.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully create io completion queue.
+ @return others - Fail to create io completion queue.
+
+**/
+STATIC
+EFI_STATUS
+NvmeCreateIoCompletionQueue (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_CRIOCQ CrIoCq;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_OPC;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ CrIoCq.Qid = NVME_IO_QUEUE;
+ CrIoCq.Qsize = NVME_CCQ_SIZE;
+ CrIoCq.Pc = 1;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Create io submission queue.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully create io submission queue.
+ @return others - Fail to create io submission queue.
+
+**/
+STATIC
+EFI_STATUS
+NvmeCreateIoSubmissionQueue (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_CRIOSQ CrIoSq;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_OPC;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ CrIoSq.Qid = NVME_IO_QUEUE;
+ CrIoSq.Qsize = NVME_CSQ_SIZE;
+ CrIoSq.Pc = 1;
+ CrIoSq.Cqid = NVME_IO_QUEUE;
+ CrIoSq.Qprio = 0;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Security send and receive commands.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] SendCommand - The flag to indicate the command type, TRUE for Send command and FALSE for receive command
+ @param[in] SecurityProtocol - Security Protocol
+ @param[in] SpSpecific - Security Protocol Specific
+ @param[in] TransferLength - Transfer Length of Buffer (in bytes) - always a multiple of 512
+ @param[in,out] TransferBuffer - Address of Data to transfer
+
+ @return EFI_SUCCESS - Successfully create io submission queue.
+ @return others - Fail to send/receive commands.
+
+**/
+EFI_STATUS
+NvmeSecuritySendReceive (
+ IN NVME_CONTEXT *Nvme,
+ IN BOOLEAN SendCommand,
+ IN UINT8 SecurityProtocol,
+ IN UINT16 SpSpecific,
+ IN UINTN TransferLength,
+ IN OUT VOID *TransferBuffer
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_SECSEND SecSend;
+ OACS *Oacs;
+ UINT8 Opcode;
+ VOID* *SecBuff;
+
+ Oacs = (OACS *)&Nvme->ControllerData->Oacs;
+
+ //
+ // Verify security bit for Security Send/Receive commands
+ //
+ if (Oacs->Security == 0) {
+ DEBUG ((DEBUG_ERROR, "Security command doesn't support.\n"));
+ return EFI_NOT_READY;
+ }
+
+ SecBuff = (VOID *)(UINTN) NVME_SEC_BASE (Nvme);
+
+ //
+ // Actions for sending security command
+ //
+ if (SendCommand) {
+#if (EN_NVME_VERBOSE_DBINFO == ON)
+ //DumpMem (TransferBuffer, TransferLength);
+#endif
+ CopyMem (SecBuff, TransferBuffer, TransferLength);
+ }
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&SecSend, sizeof(NVME_ADMIN_SECSEND));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Opcode = (UINT8)(SendCommand ? NVME_ADMIN_SECURITY_SEND_OPC : NVME_ADMIN_SECURITY_RECV_OPC);
+ Command.Cdw0.Opcode = Opcode;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)SecBuff;
+ CommandPacket.TransferLength = (UINT32)TransferLength;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ SecSend.Spsp = SpSpecific;
+ SecSend.Secp = SecurityProtocol;
+ SecSend.Tl = (UINT32)TransferLength;
+
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &SecSend, sizeof (NVME_ADMIN_SECSEND));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ //
+ // Actions for receiving security command
+ //
+ if (!SendCommand) {
+ CopyMem (TransferBuffer, SecBuff, TransferLength);
+#if (EN_NVME_VERBOSE_DBINFO == ON)
+ //DumpMem (TransferBuffer, TransferLength);
+#endif
+ }
+
+ return Status;
+}
+
+/**
+ Destroy io completion queue.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully destroy io completion queue.
+ @return others - Fail to destroy io completion queue.
+
+**/
+STATIC
+EFI_STATUS
+NvmeDestroyIoCompletionQueue (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_DEIOCQ DelIoCq;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&DelIoCq, sizeof(NVME_ADMIN_DEIOCQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_DELIOCQ_OPC;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->CqBuffer[NVME_IO_QUEUE];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ DelIoCq.Qid = NVME_IO_QUEUE;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoCq, sizeof (NVME_ADMIN_DEIOCQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Destroy io submission queue.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @return EFI_SUCCESS - Successfully destroy io submission queue.
+ @return others - Fail to destroy io submission queue.
+
+**/
+STATIC
+EFI_STATUS
+NvmeDestroyIoSubmissionQueue (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ NVME_ADMIN_DEIOSQ DelIoSq;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+ ZeroMem (&DelIoSq, sizeof(NVME_ADMIN_DEIOSQ));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ Command.Cdw0.Opcode = NVME_ADMIN_DELIOSQ_OPC;
+ Command.Cdw0.Cid = Nvme->Cid[NVME_ADMIN_QUEUE]++;
+ CommandPacket.TransferBuffer = (UINT64)(UINTN)Nvme->SqBuffer[NVME_IO_QUEUE];
+ CommandPacket.TransferLength = EFI_PAGE_SIZE;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_ADMIN_QUEUE;
+
+ DelIoSq.Qid = NVME_IO_QUEUE;
+ CopyMem (&CommandPacket.NvmeCmd->Cdw10, &DelIoSq, sizeof (NVME_ADMIN_DEIOSQ));
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ NVME_CONTROLLER_ID,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Allocate transfer-related Data struct which is used at Nvme.
+
+ @param[in] ImageHandle Image handle for this driver image
+ @param[in] Nvme The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_OUT_OF_RESOURCE The allocation is failure.
+ @retval EFI_SUCCESS Successful to allocate memory.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeAllocateResource (
+ IN EFI_HANDLE ImageHandle,
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Addr;
+ UINT32 Size;
+
+ //
+ // Allocate resources required by NVMe host controller.
+ //
+ // NBAR
+ Size = 0x10000;
+ Addr = 0xFFFFFFFF;
+ Status = gDS->AllocateMemorySpace (
+ EfiGcdAllocateMaxAddressSearchBottomUp,
+ EfiGcdMemoryTypeMemoryMappedIo,
+ 15, // 2^15: 32K Alignment
+ Size,
+ &Addr,
+ ImageHandle,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Nvme->Nbar = (UINT32) Addr;
+
+ // DMA Buffer
+ Size = NVME_MEM_MAX_SIZE;
+ Addr = 0xFFFFFFFF;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (Size),
+ (EFI_PHYSICAL_ADDRESS *)&Addr
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Nvme->BaseMem = (UINT32) Addr;
+
+ // Clean up DMA Buffer before using
+ ZeroMem ((VOID *)(UINTN)Addr, NVME_MEM_MAX_SIZE);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Free allocated transfer-related Data struct which is used at NVMe.
+
+ @param[in] Nvme The pointer to the NVME_CONTEXT Data structure.
+
+**/
+VOID
+EFIAPI
+NvmeFreeResource (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ UINT32 Size;
+
+ // NBAR
+ if (Nvme->BaseMem != 0) {
+ Size = 0x10000;
+ gDS->FreeMemorySpace (Nvme->Nbar, Size);
+ }
+
+ // DMA Buffer
+ if (Nvme->Nbar != 0) {
+ Size = NVME_MEM_MAX_SIZE;
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN) Nvme->Nbar, EFI_SIZE_TO_PAGES (Size));
+ }
+}
+
+
+/**
+ Initialize the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_SUCCESS - The NVM Express Controller is initialized successfully.
+ @retval Others - A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerInit (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ EFI_STATUS Status;
+ NVME_AQA Aqa;
+ NVME_ASQ Asq;
+ NVME_ACQ Acq;
+ NVME_VER Ver;
+
+ UINT32 MlBAR;
+ UINT32 MuBAR;
+
+ ///
+ /// Update PCIE BAR0/1 for NVME device
+ ///
+ MlBAR = Nvme->Nbar;
+ MuBAR = 0;
+ PciWrite32 (Nvme->PciBase + 0x10, MlBAR); // MLBAR (BAR0)
+ PciWrite32 (Nvme->PciBase + 0x14, MuBAR); // MUBAR (BAR1)
+
+ ///
+ /// Enable PCIE decode
+ ///
+ PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x6);
+
+ // Version
+ NVME_GET_VER (Nvme, &Ver);
+ if (!(Ver.Mjr == 0x0001) && (Ver.Mnr == 0x0000)) {
+ DEBUG ((DEBUG_INFO, "\n!!!\n!!! NVME Version mismatch for the implementation !!!\n!!!\n"));
+ }
+
+ ///
+ /// Read the Controller Capabilities register and verify that the NVM command set is supported
+ ///
+ Status = NVME_GET_CAP (Nvme, &Nvme->Cap);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "NVME_GET_CAP fail, Status: %r\n", Status));
+ goto Done;
+ }
+
+ if (Nvme->Cap.Css != 0x01) {
+ DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: the controller doesn't support NVMe command set\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ ///
+ /// Currently the driver only supports 4k page Size.
+ ///
+ if ((Nvme->Cap.Mpsmin + 12) > EFI_PAGE_SHIFT) {
+ DEBUG ((DEBUG_ERROR, "NvmeControllerInit fail: only supports 4k page Size\n"));
+ ASSERT (FALSE);
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ Nvme->Cid[0] = 0;
+ Nvme->Cid[1] = 0;
+
+ Status = NvmeDisableController (Nvme);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeDisableController fail, Status: %r\n", Status));
+ goto Done;
+ }
+
+ ///
+ /// set number of entries admin submission & completion queues.
+ ///
+ Aqa.Asqs = NVME_ASQ_SIZE;
+ Aqa.Rsvd1 = 0;
+ Aqa.Acqs = NVME_ACQ_SIZE;
+ Aqa.Rsvd2 = 0;
+
+ ///
+ /// Address of admin submission queue.
+ ///
+ Asq = (UINT64)(UINTN)(NVME_ASQ_BASE (Nvme) & ~0xFFF);
+
+ ///
+ /// Address of admin completion queue.
+ ///
+ Acq = (UINT64)(UINTN)(NVME_ACQ_BASE (Nvme) & ~0xFFF);
+
+ ///
+ /// Address of I/O submission & completion queue.
+ ///
+ Nvme->SqBuffer[0] = (NVME_SQ *)(UINTN)NVME_ASQ_BASE (Nvme); // NVME_ADMIN_QUEUE
+ Nvme->CqBuffer[0] = (NVME_CQ *)(UINTN)NVME_ACQ_BASE (Nvme); // NVME_ADMIN_QUEUE
+ Nvme->SqBuffer[1] = (NVME_SQ *)(UINTN)NVME_SQ_BASE (Nvme, 0); // NVME_IO_QUEUE
+ Nvme->CqBuffer[1] = (NVME_CQ *)(UINTN)NVME_CQ_BASE (Nvme, 0); // NVME_IO_QUEUE
+
+ DEBUG ((DEBUG_INFO, "BaseMem = [%08X]\n", Nvme->BaseMem));
+ DEBUG ((DEBUG_INFO, "Admin Submission Queue Size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));
+ DEBUG ((DEBUG_INFO, "Admin Completion Queue Size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));
+ DEBUG ((DEBUG_INFO, "Admin Submission Queue (SqBuffer[0]) = [%08X]\n", Nvme->SqBuffer[0]));
+ DEBUG ((DEBUG_INFO, "Admin Completion Queue (CqBuffer[0]) = [%08X]\n", Nvme->CqBuffer[0]));
+ DEBUG ((DEBUG_INFO, "I/O Submission Queue (SqBuffer[1]) = [%08X]\n", Nvme->SqBuffer[1]));
+ DEBUG ((DEBUG_INFO, "I/O Completion Queue (CqBuffer[1]) = [%08X]\n", Nvme->CqBuffer[1]));
+
+ ///
+ /// Program admin queue attributes.
+ ///
+ Status = NVME_SET_AQA (Nvme, &Aqa);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ ///
+ /// Program admin submission queue address.
+ ///
+ Status = NVME_SET_ASQ (Nvme, &Asq);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ ///
+ /// Program admin completion queue address.
+ ///
+ Status = NVME_SET_ACQ (Nvme, &Acq);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ Status = NvmeEnableController (Nvme);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ ///
+ /// Create one I/O completion queue.
+ ///
+ Status = NvmeCreateIoCompletionQueue (Nvme);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ ///
+ /// Create one I/O Submission queue.
+ ///
+ Status = NvmeCreateIoSubmissionQueue (Nvme);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ ///
+ /// Get current Identify Controller Data
+ ///
+ Nvme->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)(UINTN) NVME_CONTROL_DATA_BASE (Nvme);
+ Status = NvmeIdentifyController (Nvme, Nvme->ControllerData);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ ///
+ /// Dump NvmExpress Identify Controller Data
+ ///
+ Nvme->ControllerData->Sn[19] = 0;
+ Nvme->ControllerData->Mn[39] = 0;
+ //NvmeDumpIdentifyController (Nvme->ControllerData);
+
+ ///
+ /// Get current Identify Namespace Data
+ ///
+ Nvme->NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *)NVME_NAMESPACE_DATA_BASE (Nvme);
+ Status = NvmeIdentifyNamespace (Nvme, Nvme->Nsid, Nvme->NamespaceData);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeIdentifyNamespace fail, Status = %r\n", Status));
+ goto Done;
+ }
+
+ ///
+ /// Dump NvmExpress Identify Namespace Data
+ ///
+ if (Nvme->NamespaceData->Ncap == 0) {
+ DEBUG ((DEBUG_ERROR, "Invalid Namespace, Ncap: %lx\n", Nvme->NamespaceData->Ncap));
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ Nvme->BlockSize = NvmeGetBlockSize (Nvme);
+ Nvme->LastBlock = NvmeGetLastLba (Nvme);
+
+ Nvme->State = NvmeStatusInit;
+
+ return EFI_SUCCESS;
+
+Done:
+ return Status;
+}
+
+/**
+ Un-initialize the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_SUCCESS - The NVM Express Controller is un-initialized successfully.
+ @retval Others - A device error occurred while un-initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerExit (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ if (Nvme->State == NvmeStatusInit || Nvme->State == NvmeStatusMax) {
+ ///
+ /// Destroy I/O Submission queue.
+ ///
+ Status = NvmeDestroyIoSubmissionQueue (Nvme);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeDestroyIoSubmissionQueue fail, Status = %r\n", Status));
+ return Status;
+ }
+
+ ///
+ /// Destroy I/O completion queue.
+ ///
+ Status = NvmeDestroyIoCompletionQueue (Nvme);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeDestroyIoCompletionQueue fail, Status = %r\n", Status));
+ return Status;
+ }
+
+ Status = NvmeShutdownController (Nvme);
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeShutdownController fail, Status: %r\n", Status));
+ }
+ }
+
+ ///
+ /// Disable PCIE decode
+ ///
+ PciWrite8 (Nvme->PciBase + NVME_PCIE_PCICMD, 0x0);
+ PciWrite32 (Nvme->PciBase + 0x10, 0); // MLBAR (BAR0)
+ PciWrite32 (Nvme->PciBase + 0x14, 0); // MUBAR (BAR1)
+
+ Nvme->State = NvmeStatusUnknown;
+ return Status;
+}
+
+/**
+ Read sector Data from the NVMe device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in,out] Buffer - The Buffer used to store the Data read from the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be read.
+
+ @retval EFI_SUCCESS - Datum are read from the device.
+ @retval Others - Fail to read all the datum.
+
+**/
+EFI_STATUS
+NvmeReadSectors (
+ IN NVME_CONTEXT *Nvme,
+ IN OUT UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ )
+{
+ UINT32 Bytes;
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+
+ BlockSize = Nvme->BlockSize;
+ Bytes = Blocks * BlockSize;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;
+ CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++;
+ CommandPacket.NvmeCmd->Nsid = Nvme->Nsid;
+ CommandPacket.TransferBuffer = Buffer;
+
+ CommandPacket.TransferLength = Bytes;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_IO_QUEUE;
+
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32));
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;
+
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ Nvme->Nsid,
+ 0,
+ &CommandPacket
+ );
+
+ return Status;
+}
+
+/**
+ Write sector Data to the NVMe device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Buffer - The Buffer to be written into the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be written.
+
+ @retval EFI_SUCCESS - Datum are written into the Buffer.
+ @retval Others - Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeWriteSectors (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+ UINT32 Bytes;
+ UINT32 BlockSize;
+
+ BlockSize = Nvme->BlockSize;
+ Bytes = Blocks * BlockSize;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC;
+ CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++;
+ CommandPacket.NvmeCmd->Nsid = Nvme->Nsid;
+ CommandPacket.TransferBuffer = Buffer;
+
+ CommandPacket.TransferLength = Bytes;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_IO_QUEUE;
+
+ CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;
+ CommandPacket.NvmeCmd->Cdw11 = (UINT32)(RShiftU64 (Lba, 32));
+ CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;
+
+ CommandPacket.MetadataBuffer = (UINT64)(UINTN)NULL;
+ CommandPacket.MetadataLength = 0;
+
+ CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;
+
+ Status = NvmePassThru (
+ Nvme,
+ Nvme->Nsid,
+ 0,
+ &CommandPacket
+ );
+
+ return Status;
+}
+
+/**
+ Flushes all modified Data to the device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_SUCCESS - Datum are written into the Buffer.
+ @retval Others - Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeFlush (
+ IN NVME_CONTEXT *Nvme
+ )
+{
+ NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ NVM_EXPRESS_COMMAND Command;
+ NVM_EXPRESS_RESPONSE Response;
+ EFI_STATUS Status;
+
+ ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
+ ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
+
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeResponse = &Response;
+
+ CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC;
+ CommandPacket.NvmeCmd->Cdw0.Cid = Nvme->Cid[NVME_IO_QUEUE]++;
+ CommandPacket.NvmeCmd->Nsid = Nvme->Nsid;
+ CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
+ CommandPacket.QueueId = NVME_IO_QUEUE;
+
+ Status = NvmePassThru (
+ Nvme,
+ Nvme->Nsid,
+ 0,
+ &CommandPacket
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = NvmeWaitAllComplete (Nvme, CommandPacket.QueueId);
+ }
+
+ return Status;
+}
+
+/**
+ Read some blocks from the device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[out] Buffer - The Buffer used to store the Data read from the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be read.
+
+ @retval EFI_SUCCESS - Datum are read from the device.
+ @retval Others - Fail to read all the datum.
+
+**/
+EFI_STATUS
+NvmeRead (
+ IN NVME_CONTEXT *Nvme,
+ OUT UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ UINT32 MaxTransferBlocks;
+
+ ASSERT (Blocks <= NVME_MAX_SECTORS);
+ Status = EFI_SUCCESS;
+ BlockSize = Nvme->BlockSize;
+ if (Nvme->ControllerData->Mdts != 0) {
+ MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize;
+ } else {
+ MaxTransferBlocks = 1024;
+ }
+
+ while (Blocks > 0) {
+ if (Blocks > MaxTransferBlocks) {
+ Status = NvmeReadSectors (Nvme, Buffer, Lba, MaxTransferBlocks);
+
+ Blocks -= MaxTransferBlocks;
+ Buffer += (MaxTransferBlocks * BlockSize);
+ Lba += MaxTransferBlocks;
+ } else {
+ Status = NvmeReadSectors (Nvme, Buffer, Lba, (UINT32) Blocks);
+ Blocks = 0;
+ }
+
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeRead fail, Status = %r\n", Status));
+ break;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Write some blocks to the device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Buffer - The Buffer to be written into the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be written.
+
+ @retval EFI_SUCCESS - Datum are written into the Buffer.
+ @retval Others - Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeWrite (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ UINT32 MaxTransferBlocks;
+
+ ASSERT (Blocks <= NVME_MAX_SECTORS);
+ Status = EFI_SUCCESS;
+ BlockSize = Nvme->BlockSize;
+
+ if (Nvme->ControllerData->Mdts != 0) {
+ MaxTransferBlocks = (1 << (Nvme->ControllerData->Mdts)) * (1 << (Nvme->Cap.Mpsmin + 12)) / BlockSize;
+ } else {
+ MaxTransferBlocks = 1024;
+ }
+
+ while (Blocks > 0) {
+ if (Blocks > MaxTransferBlocks) {
+ Status = NvmeWriteSectors (Nvme, Buffer, Lba, MaxTransferBlocks);
+
+ Blocks -= MaxTransferBlocks;
+ Buffer += (MaxTransferBlocks * BlockSize);
+ Lba += MaxTransferBlocks;
+ } else {
+ Status = NvmeWriteSectors (Nvme, Buffer, Lba, (UINT32) Blocks);
+ Blocks = 0;
+ }
+
+ if (EFI_ERROR(Status)) {
+ DEBUG ((DEBUG_ERROR, "NvmeWrite fail, Status = %r\n", Status));
+ break;
+ }
+ }
+
+ return Status;
+}
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h
new file mode 100644
index 0000000000..bfa4f10413
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeMode.h
@@ -0,0 +1,456 @@
+/** @file
+ Header file for NVMe function definitions
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __OPAL_PASSWORD_NVME_MODE_H__
+#define __OPAL_PASSWORD_NVME_MODE_H__
+
+
+#include "OpalNvmeReg.h"
+
+#define NVME_MAX_SECTORS 0x10000
+//
+// QueueId
+//
+#define NVME_ADMIN_QUEUE 0x00
+#define NVME_IO_QUEUE 0x01
+
+typedef struct {
+ UINT8 Opcode;
+ UINT8 FusedOperation;
+ #define NORMAL_CMD 0x00
+ #define FUSED_FIRST_CMD 0x01
+ #define FUSED_SECOND_CMD 0x02
+ UINT16 Cid;
+} NVME_CDW0;
+
+typedef struct {
+ NVME_CDW0 Cdw0;
+ UINT8 Flags;
+ #define CDW10_VALID 0x01
+ #define CDW11_VALID 0x02
+ #define CDW12_VALID 0x04
+ #define CDW13_VALID 0x08
+ #define CDW14_VALID 0x10
+ #define CDW15_VALID 0x20
+ UINT32 Nsid;
+ UINT32 Cdw10;
+ UINT32 Cdw11;
+ UINT32 Cdw12;
+ UINT32 Cdw13;
+ UINT32 Cdw14;
+ UINT32 Cdw15;
+} NVM_EXPRESS_COMMAND;
+
+typedef struct {
+ UINT32 Cdw0;
+ UINT32 Cdw1;
+ UINT32 Cdw2;
+ UINT32 Cdw3;
+} NVM_EXPRESS_RESPONSE;
+
+typedef struct {
+ UINT64 CommandTimeout;
+ UINT64 TransferBuffer;
+ UINT32 TransferLength;
+ UINT64 MetadataBuffer;
+ UINT32 MetadataLength;
+ UINT8 QueueId;
+ NVM_EXPRESS_COMMAND *NvmeCmd;
+ NVM_EXPRESS_RESPONSE *NvmeResponse;
+} NVM_EXPRESS_PASS_THRU_COMMAND_PACKET;
+
+
+#pragma pack(1)
+
+// Internal fields
+typedef enum {
+ NvmeStatusUnknown,
+ NvmeStatusInit,
+ NvmeStatusInuse,
+ NvmeStatusMax,
+} NVME_STATUS;
+
+typedef struct {
+ UINT32 Nbar;
+ UINT32 BaseMem;
+ BOOLEAN PollCancellation;
+ UINT16 NvmeInitWaitTime;
+
+ NVME_STATUS State;
+ UINT8 BusID;
+ UINT8 DeviceID;
+ UINT8 FuncID;
+ UINTN PciBase;
+
+ UINT32 Nsid;
+ UINT64 Nsuuid;
+ UINT32 BlockSize;
+ EFI_LBA LastBlock;
+
+ //
+ // Pointers to 4kB aligned submission & completion queues.
+ //
+ NVME_SQ *SqBuffer[NVME_MAX_IO_QUEUES];
+ NVME_CQ *CqBuffer[NVME_MAX_IO_QUEUES];
+ UINT16 Cid[NVME_MAX_IO_QUEUES];
+
+ //
+ // Submission and completion queue indices.
+ //
+ NVME_SQTDBL SqTdbl[NVME_MAX_IO_QUEUES];
+ NVME_CQHDBL CqHdbl[NVME_MAX_IO_QUEUES];
+ UINT8 Pt[NVME_MAX_IO_QUEUES];
+
+ UINTN SqeCount[NVME_MAX_IO_QUEUES];
+
+ //
+ // Nvme controller capabilities
+ //
+ NVME_CAP Cap;
+
+ //
+ // pointer to identify controller Data
+ //
+ NVME_ADMIN_CONTROLLER_DATA *ControllerData;
+ NVME_ADMIN_NAMESPACE_DATA *NamespaceData;
+} NVME_CONTEXT;
+
+#pragma pack()
+
+/**
+ Transfer MMIO Data to memory.
+
+ @param[in,out] MemBuffer - Destination: Memory address
+ @param[in] MmioAddr - Source: MMIO address
+ @param[in] Size - Size for read
+
+ @retval EFI_SUCCESS - MMIO read sucessfully
+**/
+EFI_STATUS
+NvmeMmioRead (
+ IN OUT VOID *MemBuffer,
+ IN UINTN MmioAddr,
+ IN UINTN Size
+ );
+
+/**
+ Transfer memory Data to MMIO.
+
+ @param[in,out] MmioAddr - Destination: MMIO address
+ @param[in] MemBuffer - Source: Memory address
+ @param[in] Size - Size for write
+
+ @retval EFI_SUCCESS - MMIO write sucessfully
+**/
+EFI_STATUS
+NvmeMmioWrite (
+ IN OUT UINTN MmioAddr,
+ IN VOID *MemBuffer,
+ IN UINTN Size
+ );
+
+/**
+ Transfer memory data to MMIO.
+
+ @param[in,out] MmioAddr - Destination: MMIO address
+ @param[in] MemBuffer - Source: Memory address
+ @param[in] Size - Size for write
+
+ @retval EFI_SUCCESS - MMIO write sucessfully
+**/
+EFI_STATUS
+OpalPciWrite (
+ IN OUT UINTN MmioAddr,
+ IN VOID *MemBuffer,
+ IN UINTN Size
+ );
+
+/**
+ Transfer MMIO data to memory.
+
+ @param[in,out] MemBuffer - Destination: Memory address
+ @param[in] MmioAddr - Source: MMIO address
+ @param[in] Size - Size for read
+
+ @retval EFI_SUCCESS - MMIO read sucessfully
+**/
+EFI_STATUS
+OpalPciRead (
+ IN OUT VOID *MemBuffer,
+ IN UINTN MmioAddr,
+ IN UINTN Size
+ );
+
+/**
+ Allocate transfer-related Data struct which is used at Nvme.
+
+ @param[in] ImageHandle Image handle for this driver image
+ @param[in] Nvme The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_OUT_OF_RESOURCE The allocation is failure.
+ @retval EFI_SUCCESS Successful to allocate memory.
+
+**/
+EFI_STATUS
+EFIAPI
+NvmeAllocateResource (
+ IN EFI_HANDLE ImageHandle,
+ IN NVME_CONTEXT *Nvme
+ );
+
+/**
+ Free allocated transfer-related Data struct which is used at NVMe.
+
+ @param[in] Nvme The pointer to the NVME_CONTEXT Data structure.
+
+**/
+VOID
+EFIAPI
+NvmeFreeResource (
+ IN NVME_CONTEXT *Nvme
+ );
+
+/**
+ Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
+ both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the nonblocking
+ I/O functionality is optional.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] NamespaceId - Is a 32 bit Namespace ID to which the Express HCI command packet will be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace
+ ID specifies that the command packet should be sent to all valid namespaces.
+ @param[in] NamespaceUuid - Is a 64 bit Namespace UUID to which the Express HCI command packet will be sent.
+ A Value of 0 denotes the NVM Express controller, a Value of all 0FFh in the namespace
+ UUID specifies that the command packet should be sent to all valid namespaces.
+ @param[in,out] Packet - A pointer to the NVM Express HCI Command Packet to send to the NVMe namespace specified
+ by NamespaceId.
+
+ @retval EFI_SUCCESS - The NVM Express Command Packet was sent by the host. TransferLength bytes were transferred
+ to, or from DataBuffer.
+ @retval EFI_NOT_READY - The NVM Express Command Packet could not be sent because the controller is not ready. The caller
+ may retry again later.
+ @retval EFI_DEVICE_ERROR - A device error occurred while attempting to send the NVM Express Command Packet.
+ @retval EFI_INVALID_PARAMETER - Namespace, or the contents of NVM_EXPRESS_PASS_THRU_COMMAND_PACKET are invalid. The NVM
+ Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_UNSUPPORTED - The command described by the NVM Express Command Packet is not supported by the host adapter.
+ The NVM Express Command Packet was not sent, so no additional status information is available.
+ @retval EFI_TIMEOUT - A timeout occurred while waiting for the NVM Express Command Packet to execute.
+
+**/
+EFI_STATUS
+NvmePassThru (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT32 NamespaceId,
+ IN UINT64 NamespaceUuid,
+ IN OUT NVM_EXPRESS_PASS_THRU_COMMAND_PACKET *Packet
+ );
+
+/**
+ Waits until all NVME commands completed.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Qid - Queue index
+
+ @retval EFI_SUCCESS - All NVME commands have completed
+ @retval EFI_TIMEOUT - Timeout occured
+ @retval EFI_NOT_READY - Not all NVME commands have completed
+ @retval others - Error occurred on device side.
+**/
+EFI_STATUS
+NvmeWaitAllComplete (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT8 Qid
+ );
+
+/**
+ Initialize the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_SUCCESS - The NVM Express Controller is initialized successfully.
+ @retval Others - A device error occurred while initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerInit (
+ IN NVME_CONTEXT *Nvme
+ );
+
+/**
+ Un-initialize the Nvm Express controller.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_SUCCESS - The NVM Express Controller is un-initialized successfully.
+ @retval Others - A device error occurred while un-initializing the controller.
+
+**/
+EFI_STATUS
+NvmeControllerExit (
+ IN NVME_CONTEXT *Nvme
+ );
+
+/**
+ Check whether there are available command slots.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Qid - Queue index
+
+ @retval EFI_SUCCESS - Available command slot is found
+ @retval EFI_NOT_READY - No available command slot is found
+ @retval EFI_DEVICE_ERROR - Error occurred on device side.
+
+**/
+EFI_STATUS
+NvmeHasFreeCmdSlot (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT8 Qid
+ );
+
+/**
+ Check whether all command slots are clean.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Qid - Queue index
+
+ @retval EFI_SUCCESS - All command slots are clean
+ @retval EFI_NOT_READY - Not all command slots are clean
+ @retval EFI_DEVICE_ERROR - Error occurred on device side.
+
+**/
+EFI_STATUS
+NvmeIsAllCmdSlotClean (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT8 Qid
+ );
+
+/**
+ Read sector Data from the NVMe device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in,out] Buffer - The Buffer used to store the Data read from the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be read.
+
+ @retval EFI_SUCCESS - Datum are read from the device.
+ @retval Others - Fail to read all the datum.
+
+**/
+EFI_STATUS
+NvmeReadSectors (
+ IN NVME_CONTEXT *Nvme,
+ IN OUT UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ );
+
+/**
+ Write sector Data to the NVMe device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Buffer - The Buffer to be written into the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be written.
+
+ @retval EFI_SUCCESS - Datum are written into the Buffer.
+ @retval Others - Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeWriteSectors (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINT32 Blocks
+ );
+
+/**
+ Flushes all modified Data to the device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+
+ @retval EFI_SUCCESS - Datum are written into the Buffer.
+ @retval Others - Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeFlush (
+ IN NVME_CONTEXT *Nvme
+ );
+
+/**
+ Read some blocks from the device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[out] Buffer - The Buffer used to store the Data read from the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be read.
+
+ @retval EFI_SUCCESS - Datum are read from the device.
+ @retval Others - Fail to read all the datum.
+
+**/
+EFI_STATUS
+NvmeRead (
+ IN NVME_CONTEXT *Nvme,
+ OUT UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ );
+
+/**
+ Write some blocks to the device.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] Buffer - The Buffer to be written into the device.
+ @param[in] Lba - The start block number.
+ @param[in] Blocks - Total block number to be written.
+
+ @retval EFI_SUCCESS - Datum are written into the Buffer.
+ @retval Others - Fail to write all the datum.
+
+**/
+EFI_STATUS
+NvmeWrite (
+ IN NVME_CONTEXT *Nvme,
+ IN UINT64 Buffer,
+ IN UINT64 Lba,
+ IN UINTN Blocks
+ );
+
+/**
+ Security send and receive commands.
+
+ @param[in] Nvme - The pointer to the NVME_CONTEXT Data structure.
+ @param[in] SendCommand - The flag to indicate the command type, TRUE for Send command and FALSE for receive command
+ @param[in] SecurityProtocol - Security Protocol
+ @param[in] SpSpecific - Security Protocol Specific
+ @param[in] TransferLength - Transfer Length of Buffer (in bytes) - always a multiple of 512
+ @param[in,out] TransferBuffer - Address of Data to transfer
+
+ @return EFI_SUCCESS - Successfully create io submission queue.
+ @return others - Fail to send/receive commands.
+
+**/
+EFI_STATUS
+NvmeSecuritySendReceive (
+ IN NVME_CONTEXT *Nvme,
+ IN BOOLEAN SendCommand,
+ IN UINT8 SecurityProtocol,
+ IN UINT16 SpSpecific,
+ IN UINTN TransferLength,
+ IN OUT VOID *TransferBuffer
+ );
+
+#endif
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h
new file mode 100644
index 0000000000..b5460cd42e
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalNvmeReg.h
@@ -0,0 +1,814 @@
+/** @file
+ Header file for Registers and Structure definitions
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#ifndef __OPAL_PASSWORD_NVME_REG_H__
+#define __OPAL_PASSWORD_NVME_REG_H__
+
+//
+// PCI Header for PCIe root port configuration
+//
+#define NVME_PCIE_PCICMD 0x04
+#define NVME_PCIE_BNUM 0x18
+#define NVME_PCIE_SEC_BNUM 0x19
+#define NVME_PCIE_IOBL 0x1C
+#define NVME_PCIE_MBL 0x20
+#define NVME_PCIE_PMBL 0x24
+#define NVME_PCIE_PMBU32 0x28
+#define NVME_PCIE_PMLU32 0x2C
+#define NVME_PCIE_INTR 0x3C
+
+//
+// NVMe related definitions
+//
+#define PCI_CLASS_MASS_STORAGE_NVM 0x08 // mass storage sub-class non-volatile memory.
+#define PCI_IF_NVMHCI 0x02 // mass storage programming interface NVMHCI.
+
+#define NVME_ASQ_SIZE 1 // Number of admin submission queue entries, which is 0-based
+#define NVME_ACQ_SIZE 1 // Number of admin completion queue entries, which is 0-based
+
+#define NVME_CSQ_SIZE 63 // Number of I/O submission queue entries, which is 0-based
+#define NVME_CCQ_SIZE 63 // Number of I/O completion queue entries, which is 0-based
+
+#define NVME_MAX_IO_QUEUES 2 // Number of I/O queues supported by the driver, 1 for AQ, 1 for CQ
+
+#define NVME_CSQ_DEPTH (NVME_CSQ_SIZE+1)
+#define NVME_CCQ_DEPTH (NVME_CCQ_SIZE+1)
+#define NVME_PRP_SIZE (4) // Pages of PRP list
+
+#define NVME_CONTROLLER_ID 0
+
+//
+// Time out Value for Nvme transaction execution
+//
+#define NVME_GENERIC_TIMEOUT 5000000 ///< us
+#define NVME_CMD_WAIT 100 ///< us
+#define NVME_CMD_TIMEOUT 20000000 ///< us
+
+
+
+#define NVME_MEM_MAX_SIZE \
+ (( \
+ 1 /* Controller Data */ + \
+ 1 /* Identify Data */ + \
+ 1 /* ASQ */ + \
+ 1 /* ACQ */ + \
+ 1 /* SQs */ + \
+ 1 /* CQs */ + \
+ NVME_PRP_SIZE * NVME_CSQ_DEPTH /* PRPs */ \
+ ) * EFI_PAGE_SIZE)
+
+
+//
+// controller register offsets
+//
+#define NVME_CAP_OFFSET 0x0000 // Controller Capabilities
+#define NVME_VER_OFFSET 0x0008 // Version
+#define NVME_INTMS_OFFSET 0x000c // Interrupt Mask Set
+#define NVME_INTMC_OFFSET 0x0010 // Interrupt Mask Clear
+#define NVME_CC_OFFSET 0x0014 // Controller Configuration
+#define NVME_CSTS_OFFSET 0x001c // Controller Status
+#define NVME_AQA_OFFSET 0x0024 // Admin Queue Attributes
+#define NVME_ASQ_OFFSET 0x0028 // Admin Submission Queue Base Address
+#define NVME_ACQ_OFFSET 0x0030 // Admin Completion Queue Base Address
+#define NVME_SQ0_OFFSET 0x1000 // Submission Queue 0 (admin) Tail Doorbell
+#define NVME_CQ0_OFFSET 0x1004 // Completion Queue 0 (admin) Head Doorbell
+
+//
+// These register offsets are defined as 0x1000 + (N * (4 << CAP.DSTRD))
+// Get the doorbell stride bit shift Value from the controller capabilities.
+//
+#define NVME_SQTDBL_OFFSET(QID, DSTRD) 0x1000 + ((2 * (QID)) * (4 << (DSTRD))) // Submission Queue y (NVM) Tail Doorbell
+#define NVME_CQHDBL_OFFSET(QID, DSTRD) 0x1000 + (((2 * (QID)) + 1) * (4 << (DSTRD))) // Completion Queue y (NVM) Head Doorbell
+
+
+#pragma pack(1)
+
+//
+// 3.1.1 Offset 00h: CAP - Controller Capabilities
+//
+typedef struct {
+ UINT16 Mqes; // Maximum Queue Entries Supported
+ UINT8 Cqr:1; // Contiguous Queues Required
+ UINT8 Ams:2; // Arbitration Mechanism Supported
+ UINT8 Rsvd1:5;
+ UINT8 To; // Timeout
+ UINT16 Dstrd:4;
+ UINT16 Rsvd2:1;
+ UINT16 Css:4; // Command Sets Supported
+ UINT16 Rsvd3:7;
+ UINT8 Mpsmin:4;
+ UINT8 Mpsmax:4;
+ UINT8 Rsvd4;
+} NVME_CAP;
+
+//
+// 3.1.2 Offset 08h: VS - Version
+//
+typedef struct {
+ UINT16 Mnr; // Minor version number
+ UINT16 Mjr; // Major version number
+} NVME_VER;
+
+//
+// 3.1.5 Offset 14h: CC - Controller Configuration
+//
+typedef struct {
+ UINT16 En:1; // Enable
+ UINT16 Rsvd1:3;
+ UINT16 Css:3; // Command Set Selected
+ UINT16 Mps:4; // Memory Page Size
+ UINT16 Ams:3; // Arbitration Mechanism Selected
+ UINT16 Shn:2; // Shutdown Notification
+ UINT8 Iosqes:4; // I/O Submission Queue Entry Size
+ UINT8 Iocqes:4; // I/O Completion Queue Entry Size
+ UINT8 Rsvd2;
+} NVME_CC;
+
+//
+// 3.1.6 Offset 1Ch: CSTS - Controller Status
+//
+typedef struct {
+ UINT32 Rdy:1; // Ready
+ UINT32 Cfs:1; // Controller Fatal Status
+ UINT32 Shst:2; // Shutdown Status
+ UINT32 Nssro:1; // NVM Subsystem Reset Occurred
+ UINT32 Rsvd1:27;
+} NVME_CSTS;
+
+//
+// 3.1.8 Offset 24h: AQA - Admin Queue Attributes
+//
+typedef struct {
+ UINT16 Asqs:12; // Submission Queue Size
+ UINT16 Rsvd1:4;
+ UINT16 Acqs:12; // Completion Queue Size
+ UINT16 Rsvd2:4;
+} NVME_AQA;
+
+//
+// 3.1.9 Offset 28h: ASQ - Admin Submission Queue Base Address
+//
+#define NVME_ASQ UINT64
+
+//
+// 3.1.10 Offset 30h: ACQ - Admin Completion Queue Base Address
+//
+#define NVME_ACQ UINT64
+
+//
+// 3.1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL - Submission Queue y Tail Doorbell
+//
+typedef struct {
+ UINT16 Sqt;
+ UINT16 Rsvd1;
+} NVME_SQTDBL;
+
+//
+// 3.1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL - Completion Queue y Head Doorbell
+//
+typedef struct {
+ UINT16 Cqh;
+ UINT16 Rsvd1;
+} NVME_CQHDBL;
+
+//
+// NVM command set structures
+//
+// Read Command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting Sector Address */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Sectors */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Af:4; /* Access Frequency */
+ UINT32 Al:2; /* Access Latency */
+ UINT32 Sr:1; /* Sequential Request */
+ UINT32 In:1; /* Incompressible */
+ UINT32 Rsvd2:24;
+ //
+ // CDW 14
+ //
+ UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Elbat; /* Expected Logical Block Application Tag */
+ UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */
+} NVME_READ;
+
+//
+// Write Command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting Sector Address */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Sectors */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Af:4; /* Access Frequency */
+ UINT32 Al:2; /* Access Latency */
+ UINT32 Sr:1; /* Sequential Request */
+ UINT32 In:1; /* Incompressible */
+ UINT32 Rsvd2:24;
+ //
+ // CDW 14
+ //
+ UINT32 Ilbrt; /* Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Lbat; /* Logical Block Application Tag */
+ UINT16 Lbatm; /* Logical Block Application Tag Mask */
+} NVME_WRITE;
+
+//
+// Flush
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Flush; /* Flush */
+} NVME_FLUSH;
+
+//
+// Write Uncorrectable command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting LBA */
+ //
+ // CDW 12
+ //
+ UINT32 Nlb:16; /* Number of Logical Blocks */
+ UINT32 Rsvd1:16;
+} NVME_WRITE_UNCORRECTABLE;
+
+//
+// Write Zeroes command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting LBA */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Logical Blocks */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Rsvd2;
+ //
+ // CDW 14
+ //
+ UINT32 Ilbrt; /* Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Lbat; /* Logical Block Application Tag */
+ UINT16 Lbatm; /* Logical Block Application Tag Mask */
+} NVME_WRITE_ZEROES;
+
+//
+// Compare command
+//
+typedef struct {
+ //
+ // CDW 10, 11
+ //
+ UINT64 Slba; /* Starting LBA */
+ //
+ // CDW 12
+ //
+ UINT16 Nlb; /* Number of Logical Blocks */
+ UINT16 Rsvd1:10;
+ UINT16 Prinfo:4; /* Protection Info Check */
+ UINT16 Fua:1; /* Force Unit Access */
+ UINT16 Lr:1; /* Limited Retry */
+ //
+ // CDW 13
+ //
+ UINT32 Rsvd2;
+ //
+ // CDW 14
+ //
+ UINT32 Eilbrt; /* Expected Initial Logical Block Reference Tag */
+ //
+ // CDW 15
+ //
+ UINT16 Elbat; /* Expected Logical Block Application Tag */
+ UINT16 Elbatm; /* Expected Logical Block Application Tag Mask */
+} NVME_COMPARE;
+
+typedef union {
+ NVME_READ Read;
+ NVME_WRITE Write;
+ NVME_FLUSH Flush;
+ NVME_WRITE_UNCORRECTABLE WriteUncorrectable;
+ NVME_WRITE_ZEROES WriteZeros;
+ NVME_COMPARE Compare;
+} NVME_CMD;
+
+typedef struct {
+ UINT16 Mp; /* Maximum Power */
+ UINT8 Rsvd1; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Mps:1; /* Max Power Scale */
+ UINT8 Nops:1; /* Non-Operational State */
+ UINT8 Rsvd2:6; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Enlat; /* Entry Latency */
+ UINT32 Exlat; /* Exit Latency */
+ UINT8 Rrt:5; /* Relative Read Throughput */
+ UINT8 Rsvd3:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rrl:5; /* Relative Read Leatency */
+ UINT8 Rsvd4:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rwt:5; /* Relative Write Throughput */
+ UINT8 Rsvd5:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rwl:5; /* Relative Write Leatency */
+ UINT8 Rsvd6:3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 Rsvd7[16]; /* Reserved as of Nvm Express 1.1 Spec */
+} NVME_PSDESCRIPTOR;
+
+//
+// Identify Controller Data
+//
+typedef struct {
+ //
+ // Controller Capabilities and Features 0-255
+ //
+ UINT16 Vid; /* PCI Vendor ID */
+ UINT16 Ssvid; /* PCI sub-system vendor ID */
+ UINT8 Sn[20]; /* Produce serial number */
+
+ UINT8 Mn[40]; /* Proeduct model number */
+ UINT8 Fr[8]; /* Firmware Revision */
+ UINT8 Rab; /* Recommended Arbitration Burst */
+ UINT8 Ieee_oiu[3]; /* Organization Unique Identifier */
+ UINT8 Cmic; /* Multi-interface Capabilities */
+ UINT8 Mdts; /* Maximum Data Transfer Size */
+ UINT8 Cntlid[2]; /* Controller ID */
+ UINT8 Rsvd1[176]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // Admin Command Set Attributes
+ //
+ UINT16 Oacs; /* Optional Admin Command Support */
+ UINT8 Acl; /* Abort Command Limit */
+ UINT8 Aerl; /* Async Event Request Limit */
+ UINT8 Frmw; /* Firmware updates */
+ UINT8 Lpa; /* Log Page Attributes */
+ UINT8 Elpe; /* Error Log Page Entries */
+ UINT8 Npss; /* Number of Power States Support */
+ UINT8 Avscc; /* Admin Vendor Specific Command Configuration */
+ UINT8 Apsta; /* Autonomous Power State Transition Attributes */
+ UINT8 Rsvd2[246]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // NVM Command Set Attributes
+ //
+ UINT8 Sqes; /* Submission Queue Entry Size */
+ UINT8 Cqes; /* Completion Queue Entry Size */
+ UINT16 Rsvd3; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Nn; /* Number of Namespaces */
+ UINT16 Oncs; /* Optional NVM Command Support */
+ UINT16 Fuses; /* Fused Operation Support */
+ UINT8 Fna; /* Format NVM Attributes */
+ UINT8 Vwc; /* Volatile Write Cache */
+ UINT16 Awun; /* Atomic Write Unit Normal */
+ UINT16 Awupf; /* Atomic Write Unit Power Fail */
+ UINT8 Nvscc; /* NVM Vendor Specific Command Configuration */
+ UINT8 Rsvd4; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT16 Acwu; /* Atomic Compare & Write Unit */
+ UINT16 Rsvd5; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Sgls; /* SGL Support */
+ UINT8 Rsvd6[164]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // I/O Command set Attributes
+ //
+ UINT8 Rsvd7[1344]; /* Reserved as of Nvm Express 1.1 Spec */
+ //
+ // Power State Descriptors
+ //
+ NVME_PSDESCRIPTOR PsDescriptor[32];
+
+ UINT8 VendorData[1024]; /* Vendor specific Data */
+} NVME_ADMIN_CONTROLLER_DATA;
+
+typedef struct {
+ UINT16 Security : 1; /* supports security send/receive commands */
+ UINT16 Format : 1; /* supports format nvm command */
+ UINT16 Firmware : 1; /* supports firmware activate/download commands */
+ UINT16 Oacs_rsvd : 13;
+ } OACS; // optional admin command support: NVME_ADMIN_CONTROLLER_DATA.Oacs
+
+typedef struct {
+ UINT16 Ms; /* Metadata Size */
+ UINT8 Lbads; /* LBA Data Size */
+ UINT8 Rp:2; /* Relative Performance */
+ #define LBAF_RP_BEST 00b
+ #define LBAF_RP_BETTER 01b
+ #define LBAF_RP_GOOD 10b
+ #define LBAF_RP_DEGRADED 11b
+ UINT8 Rsvd1:6; /* Reserved as of Nvm Express 1.1 Spec */
+} NVME_LBAFORMAT;
+
+//
+// Identify Namespace Data
+//
+typedef struct {
+ //
+ // NVM Command Set Specific
+ //
+ UINT64 Nsze; /* Namespace Size (total number of blocks in formatted namespace) */
+ UINT64 Ncap; /* Namespace Capacity (max number of logical blocks) */
+ UINT64 Nuse; /* Namespace Utilization */
+ UINT8 Nsfeat; /* Namespace Features */
+ UINT8 Nlbaf; /* Number of LBA Formats */
+ UINT8 Flbas; /* Formatted LBA Size */
+ UINT8 Mc; /* Metadata Capabilities */
+ UINT8 Dpc; /* End-to-end Data Protection capabilities */
+ UINT8 Dps; /* End-to-end Data Protection Type Settings */
+ UINT8 Nmic; /* Namespace Multi-path I/O and Namespace Sharing Capabilities */
+ UINT8 Rescap; /* Reservation Capabilities */
+ UINT8 Rsvd1[88]; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT64 Eui64; /* IEEE Extended Unique Identifier */
+ //
+ // LBA Format
+ //
+ NVME_LBAFORMAT LbaFormat[16];
+
+ UINT8 Rsvd2[192]; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT8 VendorData[3712]; /* Vendor specific Data */
+} NVME_ADMIN_NAMESPACE_DATA;
+
+//
+// NvmExpress Admin Identify Cmd
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Cns:2;
+ UINT32 Rsvd1:30;
+} NVME_ADMIN_IDENTIFY;
+
+//
+// NvmExpress Admin Create I/O Completion Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Qid:16; /* Queue Identifier */
+ UINT32 Qsize:16; /* Queue Size */
+
+ //
+ // CDW 11
+ //
+ UINT32 Pc:1; /* Physically Contiguous */
+ UINT32 Ien:1; /* Interrupts Enabled */
+ UINT32 Rsvd1:14; /* reserved as of Nvm Express 1.1 Spec */
+ UINT32 Iv:16; /* Interrupt Vector */
+} NVME_ADMIN_CRIOCQ;
+
+//
+// NvmExpress Admin Create I/O Submission Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Qid:16; /* Queue Identifier */
+ UINT32 Qsize:16; /* Queue Size */
+
+ //
+ // CDW 11
+ //
+ UINT32 Pc:1; /* Physically Contiguous */
+ UINT32 Qprio:2; /* Queue Priority */
+ UINT32 Rsvd1:13; /* Reserved as of Nvm Express 1.1 Spec */
+ UINT32 Cqid:16; /* Completion Queue ID */
+} NVME_ADMIN_CRIOSQ;
+
+//
+// NvmExpress Admin Delete I/O Completion Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT16 Qid;
+ UINT16 Rsvd1;
+} NVME_ADMIN_DEIOCQ;
+
+//
+// NvmExpress Admin Delete I/O Submission Queue
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT16 Qid;
+ UINT16 Rsvd1;
+} NVME_ADMIN_DEIOSQ;
+
+//
+// NvmExpress Admin Security Send
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Resv:8; /* Reserve */
+ UINT32 Spsp:16; /* SP Specific */
+ UINT32 Secp:8; /* Security Protocol */
+
+ //
+ // CDW 11
+ //
+ UINT32 Tl; /* Transfer Length */
+} NVME_ADMIN_SECSEND;
+
+//
+// NvmExpress Admin Abort Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Sqid:16; /* Submission Queue identifier */
+ UINT32 Cid:16; /* Command Identifier */
+} NVME_ADMIN_ABORT;
+
+//
+// NvmExpress Admin Firmware Activate Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Fs:3; /* Submission Queue identifier */
+ UINT32 Aa:2; /* Command Identifier */
+ UINT32 Rsvd1:27;
+} NVME_ADMIN_FIRMWARE_ACTIVATE;
+
+//
+// NvmExpress Admin Firmware Image Download Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Numd; /* Number of Dwords */
+ //
+ // CDW 11
+ //
+ UINT32 Ofst; /* Offset */
+} NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD;
+
+//
+// NvmExpress Admin Get Features Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Fid:8; /* Feature Identifier */
+ UINT32 Sel:3; /* Select */
+ UINT32 Rsvd1:21;
+} NVME_ADMIN_GET_FEATURES;
+
+//
+// NvmExpress Admin Get Log Page Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Lid:8; /* Log Page Identifier */
+ #define LID_ERROR_INFO
+ #define LID_SMART_INFO
+ #define LID_FW_SLOT_INFO
+ UINT32 Rsvd1:8;
+ UINT32 Numd:12; /* Number of Dwords */
+ UINT32 Rsvd2:4; /* Reserved as of Nvm Express 1.1 Spec */
+} NVME_ADMIN_GET_LOG_PAGE;
+
+//
+// NvmExpress Admin Set Features Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Fid:8; /* Feature Identifier */
+ UINT32 Rsvd1:23;
+ UINT32 Sv:1; /* Save */
+} NVME_ADMIN_SET_FEATURES;
+
+//
+// NvmExpress Admin Format NVM Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Lbaf:4; /* LBA Format */
+ UINT32 Ms:1; /* Metadata Settings */
+ UINT32 Pi:3; /* Protection Information */
+ UINT32 Pil:1; /* Protection Information Location */
+ UINT32 Ses:3; /* Secure Erase Settings */
+ UINT32 Rsvd1:20;
+} NVME_ADMIN_FORMAT_NVM;
+
+//
+// NvmExpress Admin Security Receive Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Rsvd1:8;
+ UINT32 Spsp:16; /* SP Specific */
+ UINT32 Secp:8; /* Security Protocol */
+ //
+ // CDW 11
+ //
+ UINT32 Al; /* Allocation Length */
+} NVME_ADMIN_SECURITY_RECEIVE;
+
+//
+// NvmExpress Admin Security Send Command
+//
+typedef struct {
+ //
+ // CDW 10
+ //
+ UINT32 Rsvd1:8;
+ UINT32 Spsp:16; /* SP Specific */
+ UINT32 Secp:8; /* Security Protocol */
+ //
+ // CDW 11
+ //
+ UINT32 Tl; /* Transfer Length */
+} NVME_ADMIN_SECURITY_SEND;
+
+typedef union {
+ NVME_ADMIN_IDENTIFY Identify;
+ NVME_ADMIN_CRIOCQ CrIoCq;
+ NVME_ADMIN_CRIOSQ CrIoSq;
+ NVME_ADMIN_DEIOCQ DeIoCq;
+ NVME_ADMIN_DEIOSQ DeIoSq;
+ NVME_ADMIN_ABORT Abort;
+ NVME_ADMIN_FIRMWARE_ACTIVATE Activate;
+ NVME_ADMIN_FIRMWARE_IMAGE_DOWNLOAD FirmwareImageDownload;
+ NVME_ADMIN_GET_FEATURES GetFeatures;
+ NVME_ADMIN_GET_LOG_PAGE GetLogPage;
+ NVME_ADMIN_SET_FEATURES SetFeatures;
+ NVME_ADMIN_FORMAT_NVM FormatNvm;
+ NVME_ADMIN_SECURITY_RECEIVE SecurityReceive;
+ NVME_ADMIN_SECURITY_SEND SecuritySend;
+} NVME_ADMIN_CMD;
+
+typedef struct {
+ UINT32 Cdw10;
+ UINT32 Cdw11;
+ UINT32 Cdw12;
+ UINT32 Cdw13;
+ UINT32 Cdw14;
+ UINT32 Cdw15;
+} NVME_RAW;
+
+typedef union {
+ NVME_ADMIN_CMD Admin; // Union of Admin commands
+ NVME_CMD Nvm; // Union of Nvm commands
+ NVME_RAW Raw;
+} NVME_PAYLOAD;
+
+//
+// Submission Queue
+//
+typedef struct {
+ //
+ // CDW 0, Common to all comnmands
+ //
+ UINT8 Opc; // Opcode
+ UINT8 Fuse:2; // Fused Operation
+ UINT8 Rsvd1:5;
+ UINT8 Psdt:1; // PRP or SGL for Data Transfer
+ UINT16 Cid; // Command Identifier
+
+ //
+ // CDW 1
+ //
+ UINT32 Nsid; // Namespace Identifier
+
+ //
+ // CDW 2,3
+ //
+ UINT64 Rsvd2;
+
+ //
+ // CDW 4,5
+ //
+ UINT64 Mptr; // Metadata Pointer
+
+ //
+ // CDW 6-9
+ //
+ UINT64 Prp[2]; // First and second PRP entries
+
+ NVME_PAYLOAD Payload;
+
+} NVME_SQ;
+
+//
+// Completion Queue
+//
+typedef struct {
+ //
+ // CDW 0
+ //
+ UINT32 Dword0;
+ //
+ // CDW 1
+ //
+ UINT32 Rsvd1;
+ //
+ // CDW 2
+ //
+ UINT16 Sqhd; // Submission Queue Head Pointer
+ UINT16 Sqid; // Submission Queue Identifier
+ //
+ // CDW 3
+ //
+ UINT16 Cid; // Command Identifier
+ UINT16 Pt:1; // Phase Tag
+ UINT16 Sc:8; // Status Code
+ UINT16 Sct:3; // Status Code Type
+ UINT16 Rsvd2:2;
+ UINT16 Mo:1; // More
+ UINT16 Dnr:1; // Retry
+} NVME_CQ;
+
+//
+// Nvm Express Admin cmd opcodes
+//
+#define NVME_ADMIN_DELIOSQ_OPC 0
+#define NVME_ADMIN_CRIOSQ_OPC 1
+#define NVME_ADMIN_DELIOCQ_OPC 4
+#define NVME_ADMIN_CRIOCQ_OPC 5
+#define NVME_ADMIN_IDENTIFY_OPC 6
+#define NVME_ADMIN_SECURITY_SEND_OPC 0x81
+#define NVME_ADMIN_SECURITY_RECV_OPC 0x82
+
+#define NVME_IO_FLUSH_OPC 0
+#define NVME_IO_WRITE_OPC 1
+#define NVME_IO_READ_OPC 2
+
+//
+// Offset from the beginning of private Data queue Buffer
+//
+#define NVME_ASQ_BUF_OFFSET EFI_PAGE_SIZE
+
+#pragma pack()
+
+#endif
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c
new file mode 100644
index 0000000000..57d10a17b7
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c
@@ -0,0 +1,1036 @@
+/** @file
+ Opal password smm driver which is used to support Opal security feature at s3 path.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "OpalPasswordSmm.h"
+
+#define SMM_SIZE_ALLOC_BYTES (512)
+#define RESPONSE_SIZE (200)
+
+#define PCI_CLASS_MASS_STORAGE_AHCI (0x06)
+
+#define OPAL_PCIE_ROOTPORT_SAVESIZE (0x40)
+#define STORE_INVALID_ROOTPORT_INDEX ((UINT8) -1)
+#define OPAL_DEVICE_TYPE_SATA 0x1
+#define OPAL_DEVICE_TYPE_NVME 0x2
+#define OPAL_DEVICE_TYPE_UNKNOWN 0xFF
+
+//
+// To unlock the Intel SATA controller at S3 Resume, restored the following registers.
+//
+const OPAL_HC_PCI_REGISTER_SAVE mSataHcRegisterSaveTemplate[] = {
+ {0x9, S3BootScriptWidthUint8},
+ {0x10, S3BootScriptWidthUint32},
+ {0x14, S3BootScriptWidthUint32},
+ {0x18, S3BootScriptWidthUint32},
+ {0x1C, S3BootScriptWidthUint32},
+ {0x20, S3BootScriptWidthUint32},
+ {0x24, S3BootScriptWidthUint32},
+ {0x3c, S3BootScriptWidthUint8},
+ {0x3d, S3BootScriptWidthUint8},
+ {0x40, S3BootScriptWidthUint16},
+ {0x42, S3BootScriptWidthUint16},
+ {0x92, S3BootScriptWidthUint16},
+ {0x94, S3BootScriptWidthUint32},
+ {0x9C, S3BootScriptWidthUint32},
+ {0x4, S3BootScriptWidthUint16},
+};
+
+
+UINT8 mSwSmiValue;
+LIST_ENTRY *mOpalDeviceList;
+LIST_ENTRY mSmmDeviceList = INITIALIZE_LIST_HEAD_VARIABLE (mSmmDeviceList);
+
+BOOLEAN mSendBlockSID = FALSE;
+
+// AHCI
+UINT32 mAhciBar = 0;
+EFI_AHCI_REGISTERS mAhciRegisters;
+VOID *mBuffer = NULL; // DMA can not read/write Data to smram, so we pre-allocates Buffer from AcpiNVS.
+//
+// NVME
+NVME_CONTEXT mNvmeContext;
+
+/**
+ Add new bridge node or nvme device info to the device list.
+
+ @param[in] BusNum The bus number.
+ @param[in] DevNum The device number.
+ @param[in] FuncNum The function number.
+ @param[in] Dev The device which need to add device node info.
+
+**/
+VOID
+AddPciDeviceNode (
+ UINT32 BusNum,
+ UINT32 DevNum,
+ UINT32 FuncNum,
+ OPAL_SMM_DEVICE *Dev
+ )
+{
+ UINT8 *DevList;
+ PCI_DEVICE *DeviceNode;
+
+ DevList = AllocateZeroPool (sizeof (PCI_DEVICE) + Dev->Length);
+ ASSERT (DevList != NULL);
+
+ if (Dev->Length != 0) {
+ CopyMem (DevList, Dev->PciBridgeNode, Dev->Length);
+ FreePool (Dev->PciBridgeNode);
+ }
+
+ DeviceNode = (PCI_DEVICE *) (DevList + Dev->Length);
+
+ DeviceNode->BusNum = BusNum;
+ DeviceNode->DevNum = DevNum;
+ DeviceNode->FuncNum = FuncNum;
+
+ Dev->Length += sizeof (PCI_DEVICE);
+ Dev->PciBridgeNode = (PCI_DEVICE *)DevList;
+}
+
+/**
+ Extract device info from the input device path.
+
+ @param[in] DevicePath Device path info for the device.
+ @param[in,out] Dev The device which new inputed.
+
+**/
+VOID
+ExtractDeviceInfoFromDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN OUT OPAL_SMM_DEVICE *Dev
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevPath;
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevPath2;
+ PCI_DEVICE_PATH *PciDevPath;
+ SATA_DEVICE_PATH *SataDevPath;
+ NVME_NAMESPACE_DEVICE_PATH *NvmeDevPath;
+ UINTN BusNum;
+
+ TmpDevPath = DevicePath;
+ Dev->DeviceType = OPAL_DEVICE_TYPE_UNKNOWN;
+
+ while (!IsDevicePathEnd(TmpDevPath)) {
+ if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_SATA_DP) {
+ //
+ // SATA
+ //
+ SataDevPath = ( SATA_DEVICE_PATH* )TmpDevPath;
+ Dev->SataPort = SataDevPath->HBAPortNumber;
+ Dev->SataPortMultiplierPort = SataDevPath->PortMultiplierPortNumber;
+ Dev->DeviceType = OPAL_DEVICE_TYPE_SATA;
+ break;
+ } else if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_NVME_NAMESPACE_DP) {
+ //
+ // NVMe
+ //
+ NvmeDevPath = ( NVME_NAMESPACE_DEVICE_PATH* )TmpDevPath;
+ Dev->NvmeNamespaceId = NvmeDevPath->NamespaceId;
+ Dev->DeviceType = OPAL_DEVICE_TYPE_NVME;
+ break;
+ }
+ TmpDevPath = NextDevicePathNode (TmpDevPath);
+ }
+
+ //
+ // Get bridge node info for the nvme device.
+ //
+ BusNum = 0;
+ TmpDevPath = DevicePath;
+ TmpDevPath2 = NextDevicePathNode (DevicePath);
+ while (!IsDevicePathEnd(TmpDevPath2)) {
+ if (TmpDevPath->Type == HARDWARE_DEVICE_PATH && TmpDevPath->SubType == HW_PCI_DP) {
+ PciDevPath = (PCI_DEVICE_PATH *) TmpDevPath;
+ if ((TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_NVME_NAMESPACE_DP)||
+ (TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_SATA_DP)) {
+ Dev->BusNum = (UINT32)BusNum;
+ Dev->DevNum = PciDevPath->Device;
+ Dev->FuncNum = PciDevPath->Function;
+ } else {
+ AddPciDeviceNode((UINT32)BusNum, PciDevPath->Device, PciDevPath->Function, Dev);
+ if (TmpDevPath2->Type == HARDWARE_DEVICE_PATH && TmpDevPath2->SubType == HW_PCI_DP) {
+ BusNum = PciRead8 (PCI_LIB_ADDRESS (BusNum, PciDevPath->Device, PciDevPath->Function, NVME_PCIE_SEC_BNUM));
+ }
+ }
+ }
+
+ TmpDevPath = NextDevicePathNode (TmpDevPath);
+ TmpDevPath2 = NextDevicePathNode (TmpDevPath2);
+ }
+}
+
+/**
+
+ The function returns whether or not the device is Opal Locked.
+ TRUE means that the device is partially or fully locked.
+ This will perform a Level 0 Discovery and parse the locking feature descriptor
+
+ @param[in] OpalDev Opal object to determine if locked
+
+**/
+BOOLEAN
+IsOpalDeviceLocked(
+ OPAL_SMM_DEVICE *OpalDev
+ )
+{
+ OPAL_SESSION Session;
+ OPAL_DISK_SUPPORT_ATTRIBUTE SupportedAttributes;
+ TCG_LOCKING_FEATURE_DESCRIPTOR LockingFeature;
+ UINT16 OpalBaseComId;
+ TCG_RESULT Ret;
+
+ Session.Sscp = &OpalDev->Sscp;
+ Session.MediaId = 0;
+
+ Ret = OpalGetSupportedAttributesInfo (&Session, &SupportedAttributes, &OpalBaseComId);
+ if (Ret != TcgResultSuccess) {
+ return FALSE;
+ }
+
+ OpalDev->OpalBaseComId = OpalBaseComId;
+ Session.OpalBaseComId = OpalBaseComId;
+
+ Ret = OpalGetLockingInfo(&Session, &LockingFeature);
+ if (Ret != TcgResultSuccess) {
+ return FALSE;
+ }
+
+ return OpalDeviceLocked (&SupportedAttributes, &LockingFeature);
+}
+
+/**
+ Save/Restore RootPort configuration space.
+
+ @param[in] DeviceNode - The device node.
+ @param[in] SaveAction - TRUE: Save, FALSE: Restore
+ @param[in,out] PcieConfBufferList - Configuration space data buffer for save/restore
+
+ @retval - PCIE base address of this RootPort
+**/
+UINTN
+SaveRestoreRootportConfSpace (
+ IN OPAL_SMM_DEVICE *DeviceNode,
+ IN BOOLEAN SaveAction,
+ IN OUT UINT8 **PcieConfBufferList
+ )
+{
+ UINTN RpBase;
+ UINTN Length;
+ PCI_DEVICE *DevNode;
+ UINT8 *StorePcieConfData;
+ UINTN Index;
+
+ Length = 0;
+ Index = 0;
+ RpBase = 0;
+
+ while (Length < DeviceNode->Length) {
+ DevNode = (PCI_DEVICE *)((UINT8*)DeviceNode->PciBridgeNode + Length);
+ RpBase = PCI_LIB_ADDRESS (DevNode->BusNum, DevNode->DevNum, DevNode->FuncNum, 0x0);
+
+ if (PcieConfBufferList != NULL) {
+ if (SaveAction) {
+ StorePcieConfData = (UINT8 *) AllocateZeroPool (OPAL_PCIE_ROOTPORT_SAVESIZE);
+ ASSERT (StorePcieConfData != NULL);
+ OpalPciRead (StorePcieConfData, RpBase, OPAL_PCIE_ROOTPORT_SAVESIZE);
+ PcieConfBufferList[Index] = StorePcieConfData;
+ } else {
+ // Skip PCIe Command & Status registers
+ StorePcieConfData = PcieConfBufferList[Index];
+ OpalPciWrite (RpBase, StorePcieConfData, 4);
+ OpalPciWrite (RpBase + 8, StorePcieConfData + 8, OPAL_PCIE_ROOTPORT_SAVESIZE - 8);
+
+ FreePool (StorePcieConfData);
+ }
+ }
+
+ Length += sizeof (PCI_DEVICE);
+ Index ++;
+ }
+
+ return RpBase;
+}
+
+/**
+ Configure RootPort for downstream PCIe NAND devices.
+
+ @param[in] RpBase - PCIe configuration space address of this RootPort
+ @param[in] BusNumber - Bus number
+ @param[in] MemoryBase - Memory base address
+ @param[in] MemoryLength - Memory size
+
+**/
+VOID
+ConfigureRootPortForPcieNand (
+ IN UINTN RpBase,
+ IN UINTN BusNumber,
+ IN UINT32 MemoryBase,
+ IN UINT32 MemoryLength
+ )
+{
+ UINT32 MemoryLimit;
+
+ DEBUG ((DEBUG_INFO, "ConfigureRootPortForPcieNand, BusNumber: %x, MemoryBase: %x, MemoryLength: %x\n",
+ BusNumber, MemoryBase, MemoryLength));
+
+ if (MemoryLength == 0) {
+ MemoryLimit = MemoryBase;
+ } else {
+ MemoryLimit = MemoryBase + MemoryLength + 0xFFFFF; // 1M
+ }
+
+ ///
+ /// Configue PCIE configuration space for RootPort
+ ///
+ PciWrite8 (RpBase + NVME_PCIE_BNUM + 1, (UINT8) BusNumber); // Secondary Bus Number registers
+ PciWrite8 (RpBase + NVME_PCIE_BNUM + 2, (UINT8) BusNumber); // Subordinate Bus Number registers
+ PciWrite8 (RpBase + NVME_PCIE_IOBL, 0xFF); // I/O Base registers
+ PciWrite8 (RpBase + NVME_PCIE_IOBL + 1, 0x00); // I/O Limit registers
+ PciWrite16 (RpBase + NVME_PCIE_MBL, (UINT16) RShiftU64 ((UINTN)MemoryBase, 16)); // Memory Base register
+ PciWrite16 (RpBase + NVME_PCIE_MBL + 2, (UINT16) RShiftU64 ((UINTN)MemoryLimit, 16)); // Memory Limit register
+ PciWrite16 (RpBase + NVME_PCIE_PMBL, 0xFFFF); // Prefetchable Memory Base registers
+ PciWrite16 (RpBase + NVME_PCIE_PMBL + 2, 0x0000); // Prefetchable Memory Limit registers
+ PciWrite32 (RpBase + NVME_PCIE_PMBU32, 0xFFFFFFFF); // Prefetchable Memory Upper Base registers
+ PciWrite32 (RpBase + NVME_PCIE_PMLU32, 0x00000000); // Prefetchable Memory Upper Limit registers
+}
+
+
+/**
+ Dispatch function for a Software SMI handler.
+
+ @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
+ @param[in] RegisterContext Points to an optional handler context which was specified when the
+ handler was registered.
+ @param[in, out] CommBuffer A pointer to a collection of Data in memory that will
+ be conveyed from a non-SMM environment into an SMM environment.
+ @param[in, out] CommBufferSize The Size of the CommBuffer.
+
+ @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
+ should still be called.
+ @retval Others Other execution results.
+**/
+EFI_STATUS
+EFIAPI
+SmmUnlockOpalPassword (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *RegisterContext,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ EFI_STATUS Status;
+ OPAL_SMM_DEVICE *OpalDev;
+ LIST_ENTRY *Entry;
+ UINT8 BaseClassCode;
+ UINT8 SubClassCode;
+ UINT8 ProgInt;
+ TCG_RESULT Result;
+ UINT8 SataCmdSt;
+ UINT8 *StorePcieConfDataList[16];
+ UINTN RpBase;
+ UINTN MemoryBase;
+ UINTN MemoryLength;
+ OPAL_SESSION Session;
+
+ ZeroMem (StorePcieConfDataList, sizeof (StorePcieConfDataList));
+ Status = EFI_DEVICE_ERROR;
+
+ //
+ // try to unlock all locked hdd disks.
+ //
+ for (Entry = mSmmDeviceList.ForwardLink; Entry != &mSmmDeviceList; Entry = Entry->ForwardLink) {
+ OpalDev = BASE_CR(Entry, OPAL_SMM_DEVICE, Link);
+
+ RpBase = 0;
+ SataCmdSt = 0;
+
+ ///
+ /// Configure RootPort for PCIe AHCI/NVME devices.
+ ///
+ if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ ///
+ /// Save original RootPort configuration space to heap
+ ///
+ RpBase = SaveRestoreRootportConfSpace (
+ OpalDev,
+ TRUE,
+ StorePcieConfDataList
+ );
+ MemoryBase = mNvmeContext.Nbar;
+ MemoryLength = 0;
+ ConfigureRootPortForPcieNand (RpBase, OpalDev->BusNum, (UINT32) MemoryBase, (UINT32) MemoryLength);
+
+ ///
+ /// Enable PCIE decode for RootPort
+ ///
+ SataCmdSt = PciRead8 (RpBase + NVME_PCIE_PCICMD);
+ PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0x6);
+ } else {
+ SataCmdSt = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD));
+ PciWrite8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD), 0x6);
+ }
+
+ BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0B));
+ SubClassCode = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0A));
+ ProgInt = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x09));
+ if (BaseClassCode != PCI_CLASS_MASS_STORAGE) {
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ Status = EFI_DEVICE_ERROR;
+ if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_SATA) {
+ if ((SubClassCode == PCI_CLASS_MASS_STORAGE_AHCI) || (SubClassCode == PCI_CLASS_MASS_STORAGE_RAID)) {
+ Status = GetAhciBaseAddress (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "GetAhciBaseAddress error, Status: %r\n", Status));
+ goto done;
+ }
+ Status = AhciModeInitialize ((UINT8)OpalDev->SataPort);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "AhciModeInitialize error, Status: %r\n", Status));
+ goto done;
+ }
+ } else {
+ DEBUG ((DEBUG_ERROR, "SubClassCode not support for SATA device\n"));
+ }
+ } else if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ if (SubClassCode == PCI_CLASS_MASS_STORAGE_NVM) {
+ if (ProgInt != PCI_IF_NVMHCI) {
+ DEBUG ((DEBUG_ERROR, "PI not support, skipped\n"));
+ Status = EFI_NOT_FOUND;
+ goto done;
+ }
+
+ mNvmeContext.PciBase = PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0);
+ mNvmeContext.NvmeInitWaitTime = 0;
+ mNvmeContext.Nsid = OpalDev->NvmeNamespaceId;
+ Status = NvmeControllerInit (&mNvmeContext);
+ } else {
+ DEBUG ((DEBUG_ERROR, "SubClassCode not support for NVME device\n"));
+ }
+ } else {
+ DEBUG ((DEBUG_ERROR, "Invalid Devicetype\n"));
+ goto done;
+ }
+
+ Status = EFI_DEVICE_ERROR;
+ if (IsOpalDeviceLocked(OpalDev)) {
+ ZeroMem(&Session, sizeof(Session));
+ Session.Sscp = &OpalDev->Sscp;
+ Session.MediaId = 0;
+ Session.OpalBaseComId = OpalDev->OpalBaseComId;
+
+ if (mSendBlockSID) {
+ Result = OpalBlockSid (&Session, TRUE);
+ if (Result != TcgResultSuccess) {
+ break;
+ }
+ }
+
+ Result = OpalSupportUnlock (&Session, OpalDev->Password, OpalDev->PasswordLength, NULL);
+ if (Result == TcgResultSuccess) {
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ if (SubClassCode == PCI_CLASS_MASS_STORAGE_NVM) {
+ Status = NvmeControllerExit (&mNvmeContext);
+ }
+ }
+
+done:
+ if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ ASSERT (RpBase != 0);
+ PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0);
+ RpBase = SaveRestoreRootportConfSpace (
+ OpalDev,
+ FALSE, // restore
+ StorePcieConfDataList
+ );
+ PciWrite8 (RpBase + NVME_PCIE_PCICMD, SataCmdSt);
+ } else {
+ PciWrite8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD), SataCmdSt);
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Main entry point for an SMM handler dispatch or communicate-based callback.
+
+ @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
+ @param[in] Context Points to an optional handler context which was specified when the
+ handler was registered.
+ @param[in,out] CommBuffer A pointer to a collection of Data in memory that will
+ be conveyed from a non-SMM environment into an SMM environment.
+ @param[in,out] CommBufferSize The Size of the CommBuffer.
+
+ @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
+ should still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
+ still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
+ be called.
+ @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
+**/
+EFI_STATUS
+EFIAPI
+S3SleepEntryCallBack (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *Context OPTIONAL,
+ IN OUT VOID *CommBuffer OPTIONAL,
+ IN OUT UINTN *CommBufferSize OPTIONAL
+ )
+{
+ UINTN Bus;
+ UINTN Device;
+ UINTN Function;
+ UINTN Index;
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ UINTN Offset;
+ UINT64 Address;
+ S3_BOOT_SCRIPT_LIB_WIDTH Width;
+ UINT32 Data;
+ OPAL_DISK_AND_PASSWORD_INFO *PciDev;
+ OPAL_HC_PCI_REGISTER_SAVE *HcRegisterSaveListPtr;
+ UINTN Count;
+ OPAL_SMM_DEVICE *SmmDev;
+
+ Data = 0;
+ Status = EFI_SUCCESS;
+
+ mOpalDeviceList = OpalSupportGetOpalDeviceList();
+
+ for (Entry = mOpalDeviceList->ForwardLink; Entry != mOpalDeviceList; Entry = Entry->ForwardLink) {
+ PciDev = BASE_CR (Entry, OPAL_DISK_AND_PASSWORD_INFO, Link);
+
+ SmmDev = AllocateZeroPool (sizeof (OPAL_SMM_DEVICE));
+ if (SmmDev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ SmmDev->Signature = OPAL_SMM_DEVICE_SIGNATURE;
+
+ ExtractDeviceInfoFromDevicePath(&PciDev->OpalDevicePath, SmmDev);
+
+ SmmDev->PasswordLength = PciDev->PasswordLength;
+ CopyMem(&(SmmDev->Password), PciDev->Password, OPAL_PASSWORD_MAX_LENGTH);
+
+ SmmDev->Sscp.ReceiveData = SecurityReceiveData;
+ SmmDev->Sscp.SendData = SecuritySendData;
+
+ InsertHeadList (&mSmmDeviceList, &SmmDev->Link);
+
+ if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ continue;
+ }
+
+ //
+ // Save register Data for S3. Sata controller only.
+ //
+ Bus = SmmDev->BusNum;
+ Device = SmmDev->DevNum;
+ Function = SmmDev->FuncNum;
+
+ ASSERT (SmmDev->DeviceType == OPAL_DEVICE_TYPE_SATA);
+ HcRegisterSaveListPtr = (OPAL_HC_PCI_REGISTER_SAVE *) mSataHcRegisterSaveTemplate;
+ Count = sizeof (mSataHcRegisterSaveTemplate) / sizeof (OPAL_HC_PCI_REGISTER_SAVE);
+
+ for (Index = 0; Index < Count; Index += 1) {
+ Offset = HcRegisterSaveListPtr[Index].Address;
+ Width = HcRegisterSaveListPtr[Index].Width;
+
+ switch (Width) {
+ case S3BootScriptWidthUint8:
+ Data = (UINT32)PciRead8 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset));
+ break;
+ case S3BootScriptWidthUint16:
+ Data = (UINT32)PciRead16 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset));
+ break;
+ case S3BootScriptWidthUint32:
+ Data = PciRead32 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset));
+ break;
+ default:
+ ASSERT (FALSE);
+ break;
+ }
+
+ Address = S3_BOOT_SCRIPT_LIB_PCI_ADDRESS (Bus, Device, Function, Offset);
+ Status = S3BootScriptSavePciCfgWrite (Width, Address, 1, &Data);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+
+ if (!IsListEmpty (mOpalDeviceList)) {
+ Status = S3BootScriptSaveIoWrite (S3BootScriptWidthUint8, 0xB2, 1, &mSwSmiValue);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
+
+/**
+ Main entry for this driver.
+
+ @param ImageHandle Image handle this driver.
+ @param SystemTable Pointer to SystemTable.
+
+ @retval EFI_SUCESS This function always complete successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+OpalPasswordSmmInit (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
+ EFI_SMM_SX_DISPATCH2_PROTOCOL *SxDispatch;
+ EFI_HANDLE SwHandle;
+ EFI_SMM_SW_REGISTER_CONTEXT Context;
+ EFI_HANDLE S3SleepEntryHandle;
+ EFI_SMM_SX_REGISTER_CONTEXT EntryRegisterContext;
+ EFI_SMM_VARIABLE_PROTOCOL *SmmVariable;
+ OPAL_EXTRA_INFO_VAR OpalExtraInfo;
+ UINTN DataSize;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ mBuffer = NULL;
+ SwHandle = NULL;
+ S3SleepEntryHandle = NULL;
+ ZeroMem (&mNvmeContext, sizeof (NVME_CONTEXT));
+
+ Status = gSmst->SmmLocateProtocol (
+ &gEfiSmmSwDispatch2ProtocolGuid,
+ NULL,
+ (VOID **)&SwDispatch
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG((DEBUG_ERROR, " SmmLocateProtocol gEfiSmmSwDispatch2ProtocolGuid fail, Status: %r\n", Status));
+ return Status;
+ }
+
+ Status = gSmst->SmmLocateProtocol (
+ &gEfiSmmSxDispatch2ProtocolGuid,
+ NULL,
+ (VOID **)&SxDispatch
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG((DEBUG_ERROR, " SmmLocateProtocol gEfiSmmSxDispatch2ProtocolGuid fail, Status: %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Preallocate a 512 bytes Buffer to perform trusted I/O.
+ // Assume this is big enough for unlock commands
+ // It's because DMA can not access smmram stack at the cmd execution.
+ //
+ Address = 0xFFFFFFFF;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (SMM_SIZE_ALLOC_BYTES),
+ &Address
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG((DEBUG_ERROR, " AllocatePages for SATA DAM fail, Status: %r\n", Status));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ mBuffer = (VOID *)(UINTN)Address;
+ ZeroMem ((VOID *)(UINTN)mBuffer, SMM_SIZE_ALLOC_BYTES);
+
+ //
+ // Preallocate resource for AHCI transfer descriptor.
+ //
+ Status = AhciAllocateResource ();
+ if (EFI_ERROR (Status)) {
+ DEBUG((DEBUG_ERROR, " AhciAllocateResource fail, Status: %r\n", Status));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ //
+ // Preallocate resource for NVMe configuration space.
+ //
+ Status = NvmeAllocateResource (ImageHandle, &mNvmeContext);
+ if (EFI_ERROR (Status)) {
+ DEBUG((DEBUG_ERROR, " NvmeAllocateResource fail, Status: %r\n", Status));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ //
+ // Register a S3 entry callback function to store ATA host controller context to boot script.
+ // These boot scripts would be invoked at S3 path to recovery ATA host controller h/w context
+ // for executing HDD unlock cmd.
+ //
+ EntryRegisterContext.Type = SxS3;
+ EntryRegisterContext.Phase = SxEntry;
+ Status = SxDispatch->Register (
+ SxDispatch,
+ S3SleepEntryCallBack,
+ &EntryRegisterContext,
+ &S3SleepEntryHandle
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Register Opal password smm unlock handler
+ //
+ Context.SwSmiInputValue = (UINTN) -1;
+ Status = SwDispatch->Register (
+ SwDispatch,
+ SmmUnlockOpalPassword,
+ &Context,
+ &SwHandle
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG((DEBUG_ERROR, " SwDispatch->Register fail, Status: %r\n", Status));
+ goto EXIT;
+ }
+
+ //
+ // trigger smi to unlock hdd if it's locked.
+ //
+ mSwSmiValue = (UINT8) Context.SwSmiInputValue;
+
+ Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&SmmVariable);
+ if (!EFI_ERROR (Status)) {
+ DataSize = sizeof (OPAL_EXTRA_INFO_VAR);
+ Status = SmmVariable->SmmGetVariable (
+ OPAL_EXTRA_INFO_VAR_NAME,
+ &gOpalExtraInfoVariableGuid,
+ NULL,
+ &DataSize,
+ &OpalExtraInfo
+ );
+ if (!EFI_ERROR (Status)) {
+ mSendBlockSID = OpalExtraInfo.EnableBlockSid;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+EXIT:
+ if (S3SleepEntryHandle != NULL) {
+ SxDispatch->UnRegister (SxDispatch, S3SleepEntryHandle);
+ }
+
+ AhciFreeResource ();
+
+ NvmeFreeResource (&mNvmeContext);
+
+ if (mBuffer != NULL) {
+ gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN) mBuffer, EFI_SIZE_TO_PAGES (SMM_SIZE_ALLOC_BYTES));
+ }
+
+ return Status;
+}
+
+/**
+ Provide Io action support.
+
+ @param[in] SmmDev the opal device need to perform trust io.
+ @param[in] IoType OPAL_IO_TYPE indicating whether to perform a Trusted Send or Trusted Receive.
+ @param[in] SecurityProtocol Security Protocol
+ @param[in] SpSpecific Security Protocol Specific
+ @param[in] TransferLength Transfer Length of Buffer (in bytes) - always a multiple of 512
+ @param[in] Buffer Address of Data to transfer
+
+ @retval TcgResultSuccess Perform the io action success.
+ @retval TcgResultFailure Perform the io action failed.
+
+**/
+EFI_STATUS
+PerformTrustedIo (
+ OPAL_SMM_DEVICE *SmmDev,
+ OPAL_IO_TYPE IoType,
+ UINT8 SecurityProtocol,
+ UINT16 SpSpecific,
+ UINTN TransferLength,
+ VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSizeBlocks;
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
+
+ Status = EFI_DEVICE_ERROR;
+ if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_SATA) {
+ BufferSizeBlocks = TransferLength / 512;
+
+ ZeroMem( &AtaCommandBlock, sizeof( EFI_ATA_COMMAND_BLOCK ) );
+ AtaCommandBlock.AtaCommand = ( IoType == OpalSend ) ? ATA_COMMAND_TRUSTED_SEND : ATA_COMMAND_TRUSTED_RECEIVE;
+ AtaCommandBlock.AtaSectorCount = ( UINT8 )BufferSizeBlocks;
+ AtaCommandBlock.AtaSectorNumber = ( UINT8 )( BufferSizeBlocks >> 8 );
+ AtaCommandBlock.AtaFeatures = SecurityProtocol;
+ AtaCommandBlock.AtaCylinderLow = ( UINT8 )( SpSpecific >> 8 );
+ AtaCommandBlock.AtaCylinderHigh = ( UINT8 )( SpSpecific );
+ AtaCommandBlock.AtaDeviceHead = ATA_DEVICE_LBA;
+
+
+ ZeroMem( mBuffer, HDD_PAYLOAD );
+ ASSERT( TransferLength <= HDD_PAYLOAD );
+
+ if (IoType == OpalSend) {
+ CopyMem( mBuffer, Buffer, TransferLength );
+ }
+
+ Status = AhciPioTransfer(
+ &mAhciRegisters,
+ (UINT8) SmmDev->SataPort,
+ (UINT8) SmmDev->SataPortMultiplierPort,
+ NULL,
+ 0,
+ ( IoType == OpalSend ) ? FALSE : TRUE, // i/o direction
+ &AtaCommandBlock,
+ NULL,
+ mBuffer,
+ (UINT32)TransferLength,
+ ATA_TIMEOUT
+ );
+
+ if (IoType == OpalRecv) {
+ CopyMem( Buffer, mBuffer, TransferLength );
+ }
+ } else if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ Status = NvmeSecuritySendReceive (
+ &mNvmeContext,
+ IoType == OpalSend,
+ SecurityProtocol,
+ SwapBytes16(SpSpecific),
+ TransferLength,
+ Buffer
+ );
+ } else {
+ DEBUG((DEBUG_ERROR, "DeviceType(%x) not support.\n", SmmDev->DeviceType));
+ }
+
+ return Status;
+}
+
+/**
+ Send a security protocol command to a device that receives data and/or the result
+ of one or more commands sent by SendData.
+
+ The ReceiveData function sends a security protocol command to the given MediaId.
+ The security protocol command sent is defined by SecurityProtocolId and contains
+ the security protocol specific data SecurityProtocolSpecificData. The function
+ returns the data from the security protocol command in PayloadBuffer.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero.
+
+ If the PayloadBufferSize is zero, the security protocol command is sent using the
+ Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBufferSize is too small to store the available data from the security
+ protocol command, the function shall copy PayloadBufferSize bytes into the
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+ the function shall return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function shall
+ return EFI_UNSUPPORTED. If there is no media in the device, the function returns
+ EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,
+ the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error, the
+ function shall return EFI_DEVICE_ERROR.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId ID of the medium to receive data from.
+ @param Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive data command
+ is greater than Timeout.
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param PayloadBufferSize Size in bytes of the payload data buffer.
+ @param PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command. The caller is responsible for having
+ either implicit or explicit ownership of the buffer.
+ @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the
+ data written to the payload data buffer.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available
+ data from the device. The PayloadBuffer contains the truncated data.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and
+ PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+SecurityReceiveData (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ OUT UINTN *PayloadTransferSize
+ )
+{
+ OPAL_SMM_DEVICE *SmmDev;
+
+ SmmDev = OPAL_SMM_DEVICE_FROM_THIS (This);
+ if (SmmDev == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return PerformTrustedIo (
+ SmmDev,
+ OpalRecv,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ PayloadBuffer
+ );
+}
+
+/**
+ Send a security protocol command to a device.
+
+ The SendData function sends a security protocol command containing the payload
+ PayloadBuffer to the given MediaId. The security protocol command sent is
+ defined by SecurityProtocolId and contains the security protocol specific data
+ SecurityProtocolSpecificData. If the underlying protocol command requires a
+ specific padding for the command payload, the SendData function shall add padding
+ bytes to the command payload to satisfy the padding requirements.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command is
+ sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+ return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED. If there is no media in the device, the function
+ returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the
+ device, the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall return
+ EFI_SUCCESS. If the security protocol command completes with an error, the function
+ shall return EFI_DEVICE_ERROR.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId ID of the medium to receive data from.
+ @param Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the send data command
+ is greater than Timeout.
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param PayloadBufferSize Size in bytes of the payload data buffer.
+ @param PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+SecuritySendData (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ IN VOID *PayloadBuffer
+ )
+{
+ OPAL_SMM_DEVICE *SmmDev;
+
+ SmmDev = OPAL_SMM_DEVICE_FROM_THIS (This);
+ if (SmmDev == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return PerformTrustedIo (
+ SmmDev,
+ OpalSend,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ PayloadBuffer
+ );
+
+}
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h
new file mode 100644
index 0000000000..f365136918
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.h
@@ -0,0 +1,299 @@
+/** @file
+ Opal password smm driver which is used to support Opal security feature at s3 path.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _OPAL_PASSWORD_SMM_H_
+#define _OPAL_PASSWORD_SMM_H_
+
+#include <PiSmm.h>
+#include <IndustryStandard/Atapi.h>
+
+#include <Protocol/SmmSwDispatch2.h>
+#include <Protocol/SmmSxDispatch2.h>
+#include <Protocol/AtaPassThru.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/SmmReadyToLock.h>
+#include <Protocol/SmmVariable.h>
+#include <Protocol/VariableLock.h>
+#include <Protocol/StorageSecurityCommand.h>
+
+#include <Library/OpalPasswordSupportLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+#include <Library/PciLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/S3BootScriptLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/SmmMemLib.h>
+
+#include <Library/DxeServicesTableLib.h>
+
+#include <IndustryStandard/Pci22.h>
+
+#include <Guid/OpalPasswordExtraInfoVariable.h>
+
+#include "OpalAhciMode.h"
+#include "OpalIdeMode.h"
+#include "OpalNvmeMode.h"
+
+//
+// Time out Value for ATA pass through protocol
+//
+#define ATA_TIMEOUT EFI_TIMER_PERIOD_SECONDS (3)
+
+//
+// The payload Length of HDD related ATA commands
+//
+#define HDD_PAYLOAD 512
+//
+// According to ATA spec, the max Length of hdd password is 32 bytes
+//
+#define OPAL_PASSWORD_MAX_LENGTH 32
+
+extern VOID *mBuffer;
+
+#pragma pack(1)
+
+typedef struct {
+ UINT32 Address;
+ S3_BOOT_SCRIPT_LIB_WIDTH Width;
+} OPAL_HC_PCI_REGISTER_SAVE;
+
+
+typedef struct {
+ UINT32 SegNum;
+ UINT32 BusNum;
+ UINT32 DevNum;
+ UINT32 FuncNum;
+} PCI_DEVICE;
+
+/**
+* Opal I/O Type utilized by the Trusted IO callback
+*
+* The type indicates if the I/O is a send or receive
+*/
+typedef enum {
+ //
+ // I/O is a TCG Trusted Send command
+ //
+ OpalSend,
+
+ //
+ // I/O is a TCG Trusted Receive command
+ //
+ OpalRecv
+} OPAL_IO_TYPE;
+
+
+#define OPAL_SMM_DEVICE_SIGNATURE SIGNATURE_32 ('o', 's', 'd', 's')
+
+typedef struct {
+ UINTN Signature;
+ LIST_ENTRY Link;
+
+ EFI_STORAGE_SECURITY_COMMAND_PROTOCOL Sscp;
+
+ UINT32 SegNum;
+ UINT32 BusNum;
+ UINT32 DevNum;
+ UINT32 FuncNum;
+
+ UINT8 DeviceType;
+
+ UINT32 SataPort;
+ UINT32 SataPortMultiplierPort;
+
+ UINT32 NvmeNamespaceId;
+
+ UINT8 Password[32];
+ UINT8 PasswordLength;
+
+ UINT32 Length;
+ PCI_DEVICE *PciBridgeNode;
+
+ UINT16 OpalBaseComId;
+} OPAL_SMM_DEVICE;
+
+#define OPAL_SMM_DEVICE_FROM_THIS(a) CR (a, OPAL_SMM_DEVICE, Sscp, OPAL_SMM_DEVICE_SIGNATURE)
+
+#pragma pack()
+
+/**
+ Send a security protocol command to a device that receives data and/or the result
+ of one or more commands sent by SendData.
+
+ The ReceiveData function sends a security protocol command to the given MediaId.
+ The security protocol command sent is defined by SecurityProtocolId and contains
+ the security protocol specific data SecurityProtocolSpecificData. The function
+ returns the data from the security protocol command in PayloadBuffer.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL IN command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero.
+
+ If the PayloadBufferSize is zero, the security protocol command is sent using the
+ Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBufferSize is too small to store the available data from the security
+ protocol command, the function shall copy PayloadBufferSize bytes into the
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.
+
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,
+ the function shall return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function shall
+ return EFI_UNSUPPORTED. If there is no media in the device, the function returns
+ EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,
+ the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall
+ return EFI_SUCCESS. If the security protocol command completes with an error, the
+ function shall return EFI_DEVICE_ERROR.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId ID of the medium to receive data from.
+ @param Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive data command
+ is greater than Timeout.
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param PayloadBufferSize Size in bytes of the payload data buffer.
+ @param PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command. The caller is responsible for having
+ either implicit or explicit ownership of the buffer.
+ @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the
+ data written to the payload data buffer.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available
+ data from the device. The PayloadBuffer contains the truncated data.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and
+ PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+SecurityReceiveData (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ OUT VOID *PayloadBuffer,
+ OUT UINTN *PayloadTransferSize
+ );
+
+/**
+ Send a security protocol command to a device.
+
+ The SendData function sends a security protocol command containing the payload
+ PayloadBuffer to the given MediaId. The security protocol command sent is
+ defined by SecurityProtocolId and contains the security protocol specific data
+ SecurityProtocolSpecificData. If the underlying protocol command requires a
+ specific padding for the command payload, the SendData function shall add padding
+ bytes to the command payload to satisfy the padding requirements.
+
+ For devices supporting the SCSI command set, the security protocol command is sent
+ using the SECURITY PROTOCOL OUT command defined in SPC-4.
+
+ For devices supporting the ATA command set, the security protocol command is sent
+ using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
+ is non-zero. If the PayloadBufferSize is zero, the security protocol command is
+ sent using the Trusted Non-Data command defined in ATA8-ACS.
+
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
+ return EFI_INVALID_PARAMETER.
+
+ If the given MediaId does not support security protocol commands, the function
+ shall return EFI_UNSUPPORTED. If there is no media in the device, the function
+ returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the
+ device, the function returns EFI_MEDIA_CHANGED.
+
+ If the security protocol fails to complete within the Timeout period, the function
+ shall return EFI_TIMEOUT.
+
+ If the security protocol command completes without an error, the function shall return
+ EFI_SUCCESS. If the security protocol command completes with an error, the function
+ shall return EFI_DEVICE_ERROR.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId ID of the medium to receive data from.
+ @param Timeout The timeout, in 100ns units, to use for the execution
+ of the security protocol command. A Timeout value of 0
+ means that this function will wait indefinitely for the
+ security protocol command to execute. If Timeout is greater
+ than zero, then this function will return EFI_TIMEOUT
+ if the time required to execute the receive data command
+ is greater than Timeout.
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of
+ the security protocol command to be sent.
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter
+ of the security protocol command to be sent.
+ @param PayloadBufferSize Size in bytes of the payload data buffer.
+ @param PayloadBuffer A pointer to a destination buffer to store the security
+ protocol command specific payload data for the security
+ protocol command.
+
+ @retval EFI_SUCCESS The security protocol command completed successfully.
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security
+ protocol command to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+SecuritySendData (
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN UINT64 Timeout,
+ IN UINT8 SecurityProtocolId,
+ IN UINT16 SecurityProtocolSpecificData,
+ IN UINTN PayloadBufferSize,
+ IN VOID *PayloadBuffer
+ );
+
+#endif // _OPAL_PASSWORD_SMM_H_
+
diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf
new file mode 100644
index 0000000000..32db88dacd
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.inf
@@ -0,0 +1,80 @@
+## @file
+# This is a Opal Password Smm driver.
+#
+# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = OpalPasswordSmm
+ FILE_GUID = 7D24A234-A8C2-4718-BF60-A2EF070F414E
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x0001000A
+ ENTRY_POINT = OpalPasswordSmmInit
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ OpalPasswordSmm.c
+ OpalPasswordSmm.h
+ OpalAhciMode.c
+ OpalAhciMode.h
+ OpalIdeMode.c
+ OpalIdeMode.h
+ OpalNvmeMode.c
+ OpalNvmeMode.h
+ OpalNvmeReg.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DebugLib
+ IoLib
+ PciLib
+ BaseLib
+ BaseMemoryLib
+ SmmServicesTableLib
+ MemoryAllocationLib
+ UefiLib
+ TimerLib
+ S3BootScriptLib
+ DxeServicesTableLib
+ DevicePathLib
+ OpalPasswordSupportLib
+ SmmMemLib
+
+[Guids]
+ gOpalExtraInfoVariableGuid ## CONSUMES ## GUID
+
+[Protocols]
+ gEfiSmmSwDispatch2ProtocolGuid ## CONSUMES
+ gEfiAtaPassThruProtocolGuid ## CONSUMES
+ gEfiPciIoProtocolGuid ## CONSUMES
+ gEfiSmmSxDispatch2ProtocolGuid ## CONSUMES
+ gEfiSmmVariableProtocolGuid ## CONSUMES
+ gEfiStorageSecurityCommandProtocolGuid ## CONSUMES
+
+[BuildOptions]
+ MSFT:*_*_*_CC_FLAGS = /Od /GL-
+
+[Depex]
+ gEfiSmmSwDispatch2ProtocolGuid AND
+ gEfiSmmSxDispatch2ProtocolGuid AND
+ gEfiSmmVariableProtocolGuid