]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c
SecurityPkg OpalPassword: Add solution without SMM device code
[mirror_edk2.git] / SecurityPkg / Tcg / Opal / OpalPassword / OpalPasswordPei.c
diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c
new file mode 100644 (file)
index 0000000..7f9e14f
--- /dev/null
@@ -0,0 +1,944 @@
+/** @file\r
+  Opal Password PEI driver which is used to unlock Opal Password for S3.\r
+\r
+Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "OpalPasswordPei.h"\r
+\r
+EFI_GUID mOpalDeviceAtaGuid = OPAL_DEVICE_ATA_GUID;\r
+EFI_GUID mOpalDeviceNvmeGuid = OPAL_DEVICE_NVME_GUID;\r
+\r
+#define OPAL_PCIE_ROOTPORT_SAVESIZE               (0x40)\r
+#define STORE_INVALID_ROOTPORT_INDEX              ((UINT8) -1)\r
+\r
+/**\r
+  Get IOMMU PPI.\r
+\r
+  @return Pointer to IOMMU PPI.\r
+\r
+**/\r
+EDKII_IOMMU_PPI *\r
+GetIoMmu (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EDKII_IOMMU_PPI           *IoMmu;\r
+\r
+  IoMmu = NULL;\r
+  Status = PeiServicesLocatePpi (\r
+             &gEdkiiIoMmuPpiGuid,\r
+             0,\r
+             NULL,\r
+             (VOID **) &IoMmu\r
+             );\r
+  if (!EFI_ERROR (Status) && (IoMmu != NULL)) {\r
+    return IoMmu;\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+  OperationBusMasterCommonBuffer64 mapping.\r
+\r
+  @param Pages                  The number of pages to allocate.\r
+  @param HostAddress            A pointer to store the base system memory address of the\r
+                                allocated range.\r
+  @param DeviceAddress          The resulting map address for the bus master PCI controller to use to\r
+                                access the hosts HostAddress.\r
+  @param Mapping                A resulting value to pass to Unmap().\r
+\r
+  @retval EFI_SUCCESS           The requested memory pages were allocated.\r
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are\r
+                                MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+IoMmuAllocateBuffer (\r
+  IN UINTN                  Pages,\r
+  OUT VOID                  **HostAddress,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,\r
+  OUT VOID                  **Mapping\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINTN                 NumberOfBytes;\r
+  EFI_PHYSICAL_ADDRESS  HostPhyAddress;\r
+  EDKII_IOMMU_PPI       *IoMmu;\r
+\r
+  *HostAddress = NULL;\r
+  *DeviceAddress = 0;\r
+  *Mapping = NULL;\r
+\r
+  IoMmu = GetIoMmu ();\r
+\r
+  if (IoMmu != NULL) {\r
+    Status = IoMmu->AllocateBuffer (\r
+                      IoMmu,\r
+                      EfiBootServicesData,\r
+                      Pages,\r
+                      HostAddress,\r
+                      0\r
+                      );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);\r
+    Status = IoMmu->Map (\r
+                      IoMmu,\r
+                      EdkiiIoMmuOperationBusMasterCommonBuffer,\r
+                      *HostAddress,\r
+                      &NumberOfBytes,\r
+                      DeviceAddress,\r
+                      Mapping\r
+                      );\r
+    if (EFI_ERROR (Status)) {\r
+      IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);\r
+      *HostAddress = NULL;\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    Status = IoMmu->SetAttribute (\r
+                      IoMmu,\r
+                      *Mapping,\r
+                      EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE\r
+                      );\r
+    if (EFI_ERROR (Status)) {\r
+      IoMmu->Unmap (IoMmu, *Mapping);\r
+      IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);\r
+      *Mapping = NULL;\r
+      *HostAddress = NULL;\r
+      return Status;\r
+    }\r
+  } else {\r
+    Status = PeiServicesAllocatePages (\r
+               EfiBootServicesData,\r
+               Pages,\r
+               &HostPhyAddress\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+    *HostAddress = (VOID *) (UINTN) HostPhyAddress;\r
+    *DeviceAddress = HostPhyAddress;\r
+    *Mapping = NULL;\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Frees memory that was allocated with AllocateBuffer().\r
+\r
+  @param Pages              The number of pages to free.\r
+  @param HostAddress        The base system memory address of the allocated range.\r
+  @param Mapping            The mapping value returned from Map().\r
+\r
+**/\r
+VOID\r
+IoMmuFreeBuffer (\r
+  IN UINTN                  Pages,\r
+  IN VOID                   *HostAddress,\r
+  IN VOID                   *Mapping\r
+  )\r
+{\r
+  EDKII_IOMMU_PPI       *IoMmu;\r
+\r
+  IoMmu = GetIoMmu ();\r
+\r
+  if (IoMmu != NULL) {\r
+    IoMmu->SetAttribute (IoMmu, Mapping, 0);\r
+    IoMmu->Unmap (IoMmu, Mapping);\r
+    IoMmu->FreeBuffer (IoMmu, Pages, HostAddress);\r
+  } else {\r
+    PeiServicesFreePages (\r
+      (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress,\r
+      Pages\r
+      );\r
+  }\r
+}\r
+\r
+/**\r
+  Provide IO action support.\r
+\r
+  @param[in]     PeiDev             The opal device need to perform trusted IO.\r
+  @param[in]     IoType             OPAL_IO_TYPE indicating whether to perform a Trusted Send or Trusted Receive.\r
+  @param[in]     SecurityProtocol   Security Protocol\r
+  @param[in]     SpSpecific         Security Protocol Specific\r
+  @param[in]     TransferLength     Transfer Length of Buffer (in bytes) - always a multiple of 512\r
+  @param[in]     Buffer             Address of Data to transfer\r
+\r
+  @retval        EFI_SUCCESS        Perform the IO action success.\r
+  @retval        Others             Perform the IO action failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PerformTrustedIo (\r
+  OPAL_PEI_DEVICE  *PeiDev,\r
+  OPAL_IO_TYPE     IoType,\r
+  UINT8            SecurityProtocol,\r
+  UINT16           SpSpecific,\r
+  UINTN            TransferLength,\r
+  VOID             *Buffer\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINTN                         BufferSizeBlocks;\r
+  EFI_ATA_COMMAND_BLOCK         AtaCommandBlock;\r
+  OPAL_DEVICE_ATA               *DevInfoAta;\r
+  AHCI_CONTEXT                  *AhciContext;\r
+  NVME_CONTEXT                  *NvmeContext;\r
+\r
+  Status = EFI_DEVICE_ERROR;\r
+  if (PeiDev->DeviceType == OPAL_DEVICE_TYPE_ATA) {\r
+    DevInfoAta = (OPAL_DEVICE_ATA *) PeiDev->Device;\r
+    AhciContext = (AHCI_CONTEXT *) PeiDev->Context;\r
+\r
+    BufferSizeBlocks = TransferLength / 512;\r
+\r
+    ZeroMem( &AtaCommandBlock, sizeof( EFI_ATA_COMMAND_BLOCK ) );\r
+    AtaCommandBlock.AtaCommand = ( IoType == OpalSend ) ? ATA_COMMAND_TRUSTED_SEND : ATA_COMMAND_TRUSTED_RECEIVE;\r
+    AtaCommandBlock.AtaSectorCount = ( UINT8 )BufferSizeBlocks;\r
+    AtaCommandBlock.AtaSectorNumber = ( UINT8 )( BufferSizeBlocks >> 8 );\r
+    AtaCommandBlock.AtaFeatures = SecurityProtocol;\r
+    AtaCommandBlock.AtaCylinderLow = ( UINT8 )( SpSpecific >> 8 );\r
+    AtaCommandBlock.AtaCylinderHigh = ( UINT8 )( SpSpecific );\r
+    AtaCommandBlock.AtaDeviceHead = ATA_DEVICE_LBA;\r
+\r
+\r
+    ZeroMem( AhciContext->Buffer, HDD_PAYLOAD );\r
+    ASSERT( TransferLength <= HDD_PAYLOAD );\r
+\r
+    if (IoType == OpalSend) {\r
+      CopyMem( AhciContext->Buffer, Buffer, TransferLength );\r
+    }\r
+\r
+    Status = AhciPioTransfer(\r
+                AhciContext,\r
+                (UINT8) DevInfoAta->Port,\r
+                (UINT8) DevInfoAta->PortMultiplierPort,\r
+                NULL,\r
+                0,\r
+                ( IoType == OpalSend ) ? FALSE : TRUE,   // i/o direction\r
+                &AtaCommandBlock,\r
+                NULL,\r
+                AhciContext->Buffer,\r
+                (UINT32)TransferLength,\r
+                ATA_TIMEOUT\r
+                );\r
+\r
+    if (IoType == OpalRecv) {\r
+      CopyMem( Buffer, AhciContext->Buffer, TransferLength );\r
+    }\r
+  } else if (PeiDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {\r
+    NvmeContext = (NVME_CONTEXT *) PeiDev->Context;\r
+    Status = NvmeSecuritySendReceive (\r
+                NvmeContext,\r
+                IoType == OpalSend,\r
+                SecurityProtocol,\r
+                SwapBytes16(SpSpecific),\r
+                TransferLength,\r
+                Buffer\r
+              );\r
+  } else {\r
+    DEBUG((DEBUG_ERROR, "DeviceType(%x) not support.\n", PeiDev->DeviceType));\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send a security protocol command to a device that receives data and/or the result\r
+  of one or more commands sent by SendData.\r
+\r
+  The ReceiveData function sends a security protocol command to the given MediaId.\r
+  The security protocol command sent is defined by SecurityProtocolId and contains\r
+  the security protocol specific data SecurityProtocolSpecificData. The function\r
+  returns the data from the security protocol command in PayloadBuffer.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+  For devices supporting the ATA command set, the security protocol command is sent\r
+  using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize\r
+  is non-zero.\r
+\r
+  If the PayloadBufferSize is zero, the security protocol command is sent using the\r
+  Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+  If PayloadBufferSize is too small to store the available data from the security\r
+  protocol command, the function shall copy PayloadBufferSize bytes into the\r
+  PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+  If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+  the function shall return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function shall\r
+  return EFI_UNSUPPORTED. If there is no media in the device, the function returns\r
+  EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,\r
+  the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall\r
+  return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+  function shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT\r
+                                       if the time required to execute the receive data command\r
+                                       is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command. The caller is responsible for having\r
+                                       either implicit or explicit ownership of the buffer.\r
+  @param  PayloadTransferSize          A pointer to a buffer to store the size in bytes of the\r
+                                       data written to the payload data buffer.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_WARN_BUFFER_TOO_SMALL    The PayloadBufferSize was too small to store the available\r
+                                       data from the device. The PayloadBuffer contains the truncated data.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer or PayloadTransferSize is NULL and\r
+                                       PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SecurityReceiveData (\r
+  IN  EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *This,\r
+  IN  UINT32                                   MediaId,\r
+  IN  UINT64                                   Timeout,\r
+  IN  UINT8                                    SecurityProtocolId,\r
+  IN  UINT16                                   SecurityProtocolSpecificData,\r
+  IN  UINTN                                    PayloadBufferSize,\r
+  OUT VOID                                     *PayloadBuffer,\r
+  OUT UINTN                                    *PayloadTransferSize\r
+  )\r
+{\r
+  OPAL_PEI_DEVICE               *PeiDev;\r
+\r
+  PeiDev = OPAL_PEI_DEVICE_FROM_THIS (This);\r
+  if (PeiDev == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return PerformTrustedIo (\r
+                        PeiDev,\r
+                        OpalRecv,\r
+                        SecurityProtocolId,\r
+                        SecurityProtocolSpecificData,\r
+                        PayloadBufferSize,\r
+                        PayloadBuffer\r
+                        );\r
+}\r
+\r
+/**\r
+  Send a security protocol command to a device.\r
+\r
+  The SendData function sends a security protocol command containing the payload\r
+  PayloadBuffer to the given MediaId. The security protocol command sent is\r
+  defined by SecurityProtocolId and contains the security protocol specific data\r
+  SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+  specific padding for the command payload, the SendData function shall add padding\r
+  bytes to the command payload to satisfy the padding requirements.\r
+\r
+  For devices supporting the SCSI command set, the security protocol command is sent\r
+  using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+  For devices supporting the ATA command set, the security protocol command is sent\r
+  using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize\r
+  is non-zero. If the PayloadBufferSize is zero, the security protocol command is\r
+  sent using the Trusted Non-Data command defined in ATA8-ACS.\r
+\r
+  If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+  return EFI_INVALID_PARAMETER.\r
+\r
+  If the given MediaId does not support security protocol commands, the function\r
+  shall return EFI_UNSUPPORTED. If there is no media in the device, the function\r
+  returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the\r
+  device, the function returns EFI_MEDIA_CHANGED.\r
+\r
+  If the security protocol fails to complete within the Timeout period, the function\r
+  shall return EFI_TIMEOUT.\r
+\r
+  If the security protocol command completes without an error, the function shall return\r
+  EFI_SUCCESS. If the security protocol command completes with an error, the function\r
+  shall return EFI_DEVICE_ERROR.\r
+\r
+  @param  This                         Indicates a pointer to the calling context.\r
+  @param  MediaId                      ID of the medium to receive data from.\r
+  @param  Timeout                      The timeout, in 100ns units, to use for the execution\r
+                                       of the security protocol command. A Timeout value of 0\r
+                                       means that this function will wait indefinitely for the\r
+                                       security protocol command to execute. If Timeout is greater\r
+                                       than zero, then this function will return EFI_TIMEOUT\r
+                                       if the time required to execute the send data command\r
+                                       is greater than Timeout.\r
+  @param  SecurityProtocolId           The value of the "Security Protocol" parameter of\r
+                                       the security protocol command to be sent.\r
+  @param  SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+                                       of the security protocol command to be sent.\r
+  @param  PayloadBufferSize            Size in bytes of the payload data buffer.\r
+  @param  PayloadBuffer                A pointer to a destination buffer to store the security\r
+                                       protocol command specific payload data for the security\r
+                                       protocol command.\r
+\r
+  @retval EFI_SUCCESS                  The security protocol command completed successfully.\r
+  @retval EFI_UNSUPPORTED              The given MediaId does not support security protocol commands.\r
+  @retval EFI_DEVICE_ERROR             The security protocol command completed with an error.\r
+  @retval EFI_NO_MEDIA                 There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED            The MediaId is not for the current media.\r
+  @retval EFI_INVALID_PARAMETER        The PayloadBuffer is NULL and PayloadBufferSize is non-zero.\r
+  @retval EFI_TIMEOUT                  A timeout occurred while waiting for the security\r
+                                       protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SecuritySendData (\r
+  IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL    *This,\r
+  IN UINT32                                   MediaId,\r
+  IN UINT64                                   Timeout,\r
+  IN UINT8                                    SecurityProtocolId,\r
+  IN UINT16                                   SecurityProtocolSpecificData,\r
+  IN UINTN                                    PayloadBufferSize,\r
+  IN VOID                                     *PayloadBuffer\r
+  )\r
+{\r
+  OPAL_PEI_DEVICE               *PeiDev;\r
+\r
+  PeiDev = OPAL_PEI_DEVICE_FROM_THIS (This);\r
+  if (PeiDev == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return PerformTrustedIo (\r
+                          PeiDev,\r
+                          OpalSend,\r
+                          SecurityProtocolId,\r
+                          SecurityProtocolSpecificData,\r
+                          PayloadBufferSize,\r
+                          PayloadBuffer\r
+                          );\r
+\r
+}\r
+\r
+/**\r
+  Save/Restore RootPort configuration space.\r
+\r
+  @param[in]     DevInfoNvme            Pointer to NVMe device info.\r
+  @param[in]     SaveAction             TRUE: Save, FALSE: Restore\r
+  @param[in,out] PcieConfBufferList    Configuration space data buffer for save/restore\r
+\r
+  @return PCIE base address of this RootPort\r
+**/\r
+UINTN\r
+SaveRestoreRootportConfSpace (\r
+  IN     OPAL_DEVICE_NVME               *DevInfoNvme,\r
+  IN     BOOLEAN                        SaveAction,\r
+  IN OUT UINT8                          **PcieConfBufferList\r
+  )\r
+{\r
+  UINTN             RpBase;\r
+  UINTN             Length;\r
+  OPAL_PCI_DEVICE   *DevNode;\r
+  UINT8             *StorePcieConfData;\r
+  UINTN             Index;\r
+\r
+  Length = 0;\r
+  Index  = 0;\r
+  RpBase = 0;\r
+\r
+  while (sizeof (OPAL_DEVICE_NVME) + Length < DevInfoNvme->Length) {\r
+    DevNode = (OPAL_PCI_DEVICE *)((UINT8*)DevInfoNvme->PciBridgeNode + Length);\r
+    RpBase = PCI_LIB_ADDRESS (DevNode->Bus, DevNode->Device, DevNode->Function, 0x0);\r
+\r
+    if (PcieConfBufferList != NULL) {\r
+      if (SaveAction) {\r
+        StorePcieConfData = (UINT8 *) AllocateZeroPool (OPAL_PCIE_ROOTPORT_SAVESIZE);\r
+        ASSERT (StorePcieConfData != NULL);\r
+        OpalPciRead (StorePcieConfData, RpBase, OPAL_PCIE_ROOTPORT_SAVESIZE);\r
+        PcieConfBufferList[Index] = StorePcieConfData;\r
+      } else {\r
+        // Skip PCIe Command & Status registers\r
+        StorePcieConfData = PcieConfBufferList[Index];\r
+        OpalPciWrite (RpBase, StorePcieConfData, 4);\r
+        OpalPciWrite (RpBase + 8, StorePcieConfData + 8, OPAL_PCIE_ROOTPORT_SAVESIZE - 8);\r
+\r
+        FreePool (StorePcieConfData);\r
+      }\r
+    }\r
+\r
+    Length += sizeof (OPAL_PCI_DEVICE);\r
+    Index ++;\r
+  }\r
+\r
+  return RpBase;\r
+}\r
+\r
+/**\r
+  Configure RootPort for downstream PCIe NAND devices.\r
+\r
+  @param[in] RpBase             - PCIe configuration space address of this RootPort\r
+  @param[in] BusNumber          - Bus number\r
+  @param[in] MemoryBase         - Memory base address\r
+  @param[in] MemoryLength       - Memory size\r
+\r
+**/\r
+VOID\r
+ConfigureRootPortForPcieNand (\r
+  IN UINTN   RpBase,\r
+  IN UINTN   BusNumber,\r
+  IN UINT32  MemoryBase,\r
+  IN UINT32  MemoryLength\r
+  )\r
+{\r
+  UINT32  MemoryLimit;\r
+\r
+  DEBUG ((DEBUG_INFO, "ConfigureRootPortForPcieNand, BusNumber: %x, MemoryBase: %x, MemoryLength: %x\n",\r
+    BusNumber, MemoryBase, MemoryLength));\r
+\r
+  if (MemoryLength == 0) {\r
+    MemoryLimit = MemoryBase;\r
+  } else {\r
+    MemoryLimit = MemoryBase + MemoryLength + 0xFFFFF; // 1M\r
+  }\r
+\r
+  ///\r
+  /// Configue PCIE configuration space for RootPort\r
+  ///\r
+  PciWrite8  (RpBase + NVME_PCIE_BNUM + 1,  (UINT8) BusNumber);           // Secondary Bus Number registers\r
+  PciWrite8  (RpBase + NVME_PCIE_BNUM + 2,  (UINT8) BusNumber);           // Subordinate Bus Number registers\r
+  PciWrite8  (RpBase + NVME_PCIE_IOBL,      0xFF);                        // I/O Base registers\r
+  PciWrite8  (RpBase + NVME_PCIE_IOBL + 1,  0x00);                        // I/O Limit registers\r
+  PciWrite16 (RpBase + NVME_PCIE_MBL,       (UINT16) RShiftU64 ((UINTN)MemoryBase, 16));  // Memory Base register\r
+  PciWrite16 (RpBase + NVME_PCIE_MBL + 2,   (UINT16) RShiftU64 ((UINTN)MemoryLimit, 16)); // Memory Limit register\r
+  PciWrite16 (RpBase + NVME_PCIE_PMBL,      0xFFFF);                      // Prefetchable Memory Base registers\r
+  PciWrite16 (RpBase + NVME_PCIE_PMBL + 2,  0x0000);                      // Prefetchable Memory Limit registers\r
+  PciWrite32 (RpBase + NVME_PCIE_PMBU32,    0xFFFFFFFF);                  // Prefetchable Memory Upper Base registers\r
+  PciWrite32 (RpBase + NVME_PCIE_PMLU32,    0x00000000);                  // Prefetchable Memory Upper Limit registers\r
+}\r
+\r
+/**\r
+\r
+  The function returns whether or not the device is Opal Locked.\r
+  TRUE means that the device is partially or fully locked.\r
+  This will perform a Level 0 Discovery and parse the locking feature descriptor\r
+\r
+  @param[in]      OpalDev             Opal object to determine if locked.\r
+  @param[out]     BlockSidSupported   Whether device support BlockSid feature.\r
+\r
+**/\r
+BOOLEAN\r
+IsOpalDeviceLocked(\r
+  OPAL_PEI_DEVICE    *OpalDev,\r
+  BOOLEAN            *BlockSidSupported\r
+  )\r
+{\r
+  OPAL_SESSION                   Session;\r
+  OPAL_DISK_SUPPORT_ATTRIBUTE    SupportedAttributes;\r
+  TCG_LOCKING_FEATURE_DESCRIPTOR LockingFeature;\r
+  UINT16                         OpalBaseComId;\r
+  TCG_RESULT                     Ret;\r
+\r
+  Session.Sscp = &OpalDev->Sscp;\r
+  Session.MediaId = 0;\r
+\r
+  Ret = OpalGetSupportedAttributesInfo (&Session, &SupportedAttributes, &OpalBaseComId);\r
+  if (Ret != TcgResultSuccess) {\r
+    return FALSE;\r
+  }\r
+\r
+  Session.OpalBaseComId  = OpalBaseComId;\r
+  *BlockSidSupported     = SupportedAttributes.BlockSid == 1 ? TRUE : FALSE;\r
+\r
+  Ret = OpalGetLockingInfo(&Session, &LockingFeature);\r
+  if (Ret != TcgResultSuccess) {\r
+    return FALSE;\r
+  }\r
+\r
+  return OpalDeviceLocked (&SupportedAttributes, &LockingFeature);\r
+}\r
+\r
+/**\r
+  Unlock OPAL password for S3.\r
+\r
+  @param[in] OpalDev            Opal object to unlock.\r
+\r
+**/\r
+VOID\r
+UnlockOpalPassword (\r
+  IN OPAL_PEI_DEVICE            *OpalDev\r
+  )\r
+{\r
+  TCG_RESULT                    Result;\r
+  OPAL_SESSION                  Session;\r
+  BOOLEAN                       BlockSidSupport;\r
+  UINT32                        PpStorageFlags;\r
+  BOOLEAN                       BlockSIDEnabled;\r
+\r
+  BlockSidSupport = FALSE;\r
+  if (IsOpalDeviceLocked (OpalDev, &BlockSidSupport)) {\r
+    ZeroMem(&Session, sizeof (Session));\r
+    Session.Sscp = &OpalDev->Sscp;\r
+    Session.MediaId = 0;\r
+    Session.OpalBaseComId = OpalDev->Device->OpalBaseComId;\r
+\r
+    Result = OpalUtilUpdateGlobalLockingRange (\r
+               &Session,\r
+               OpalDev->Device->Password,\r
+               OpalDev->Device->PasswordLength,\r
+               FALSE,\r
+               FALSE\r
+               );\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "%a() OpalUtilUpdateGlobalLockingRange() Result = 0x%x\n",\r
+      __FUNCTION__,\r
+      Result\r
+      ));\r
+  }\r
+\r
+  PpStorageFlags = Tcg2PhysicalPresenceLibGetManagementFlags ();\r
+  if ((PpStorageFlags & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_ENABLE_BLOCK_SID) != 0) {\r
+    BlockSIDEnabled = TRUE;\r
+  } else {\r
+    BlockSIDEnabled = FALSE;\r
+  }\r
+  if (BlockSIDEnabled && BlockSidSupport) {\r
+    ZeroMem(&Session, sizeof (Session));\r
+    Session.Sscp = &OpalDev->Sscp;\r
+    Session.MediaId = 0;\r
+    Session.OpalBaseComId = OpalDev->Device->OpalBaseComId;\r
+    Result = OpalBlockSid (&Session, TRUE);\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "%a() OpalBlockSid() Result = 0x%x\n",\r
+      __FUNCTION__,\r
+      Result\r
+      ));\r
+  }\r
+}\r
+\r
+/**\r
+  Unlock ATA OPAL password for S3.\r
+\r
+**/\r
+VOID\r
+UnlockOpalPasswordAta (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINT8                         *DevInfo;\r
+  OPAL_DEVICE_ATA               TempDevInfoAta;\r
+  OPAL_DEVICE_ATA               *DevInfoAta;\r
+  UINTN                         DevInfoLengthAta;\r
+  UINT8                         Bus;\r
+  UINT8                         Device;\r
+  UINT8                         Function;\r
+  OPAL_PEI_DEVICE               OpalDev;\r
+  UINT8                         BaseClassCode;\r
+  UINT8                         SubClassCode;\r
+  UINT8                         SataCmdSt;\r
+  AHCI_CONTEXT                  AhciContext;\r
+  UINT32                        AhciBar;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  //\r
+  // Get ATA OPAL device info from LockBox.\r
+  //\r
+  DevInfo = (UINT8 *) &TempDevInfoAta;\r
+  DevInfoLengthAta = sizeof (OPAL_DEVICE_ATA);\r
+  Status = RestoreLockBox (&mOpalDeviceAtaGuid, DevInfo, &DevInfoLengthAta);\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    DevInfo = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLengthAta));\r
+    if (DevInfo != NULL) {\r
+      Status = RestoreLockBox (&mOpalDeviceAtaGuid, DevInfo, &DevInfoLengthAta);\r
+    }\r
+  }\r
+  if (EFI_ERROR (Status) || (DevInfo == NULL)) {\r
+    return;\r
+  }\r
+\r
+  for (DevInfoAta = (OPAL_DEVICE_ATA *) DevInfo;\r
+       (UINTN) DevInfoAta < ((UINTN) DevInfo + DevInfoLengthAta);\r
+       DevInfoAta = (OPAL_DEVICE_ATA *) ((UINTN) DevInfoAta + DevInfoAta->Length)) {\r
+    Bus = DevInfoAta->Device.Bus;\r
+    Device = DevInfoAta->Device.Device;\r
+    Function = DevInfoAta->Device.Function;\r
+\r
+    SataCmdSt = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET));\r
+    PciWrite8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), 0x6);\r
+\r
+    BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B));\r
+    SubClassCode  = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A));\r
+    if ((BaseClassCode != PCI_CLASS_MASS_STORAGE) ||\r
+        ((SubClassCode != PCI_CLASS_MASS_STORAGE_SATADPA) && (SubClassCode != PCI_CLASS_MASS_STORAGE_RAID))) {\r
+      DEBUG ((DEBUG_ERROR, "%a() ClassCode/SubClassCode are not supported\n", __FUNCTION__));\r
+    } else {\r
+      AhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));\r
+      PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), DevInfoAta->BarAddr);\r
+\r
+      ZeroMem (&AhciContext, sizeof (AHCI_CONTEXT));\r
+      AhciContext.AhciBar = DevInfoAta->BarAddr;\r
+      AhciAllocateResource (&AhciContext);\r
+      Status = AhciModeInitialize (&AhciContext, (UINT8)DevInfoAta->Port);\r
+      ASSERT_EFI_ERROR (Status);\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((DEBUG_ERROR, "%a() AhciModeInitialize() error, Status: %r\n", __FUNCTION__, Status));\r
+      }\r
+\r
+      OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE;\r
+      OpalDev.Sscp.ReceiveData = SecurityReceiveData;\r
+      OpalDev.Sscp.SendData = SecuritySendData;\r
+      OpalDev.DeviceType = OPAL_DEVICE_TYPE_ATA;\r
+      OpalDev.Device = (OPAL_DEVICE_COMMON *) DevInfoAta;\r
+      OpalDev.Context = &AhciContext;\r
+\r
+      UnlockOpalPassword (&OpalDev);\r
+\r
+      AhciFreeResource (&AhciContext);\r
+      PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), AhciBar);\r
+    }\r
+    PciWrite8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), SataCmdSt);\r
+  }\r
+\r
+  ZeroMem (DevInfo, DevInfoLengthAta);\r
+  if ((UINTN) DevInfo != (UINTN) &TempDevInfoAta) {\r
+    FreePages (DevInfo, EFI_SIZE_TO_PAGES (DevInfoLengthAta));\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+  Unlock NVMe OPAL password for S3.\r
+\r
+**/\r
+VOID\r
+UnlockOpalPasswordNvme (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINT8                         *DevInfo;\r
+  OPAL_DEVICE_NVME              TempDevInfoNvme;\r
+  OPAL_DEVICE_NVME              *DevInfoNvme;\r
+  UINTN                         DevInfoLengthNvme;\r
+  UINT8                         Bus;\r
+  UINT8                         Device;\r
+  UINT8                         Function;\r
+  OPAL_PEI_DEVICE               OpalDev;\r
+  UINT8                         BaseClassCode;\r
+  UINT8                         SubClassCode;\r
+  UINT8                         ProgInt;\r
+  UINT8                         NvmeCmdSt;\r
+  UINT8                         *StorePcieConfDataList[16];\r
+  UINTN                         RpBase;\r
+  UINTN                         MemoryBase;\r
+  UINTN                         MemoryLength;\r
+  NVME_CONTEXT                  NvmeContext;\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));\r
+\r
+  //\r
+  // Get NVMe OPAL device info from LockBox.\r
+  //\r
+  DevInfo = (UINT8 *) &TempDevInfoNvme;\r
+  DevInfoLengthNvme = sizeof (OPAL_DEVICE_NVME);\r
+  Status = RestoreLockBox (&mOpalDeviceNvmeGuid, DevInfo, &DevInfoLengthNvme);\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    DevInfo = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLengthNvme));\r
+    if (DevInfo != NULL) {\r
+      Status = RestoreLockBox (&mOpalDeviceNvmeGuid, DevInfo, &DevInfoLengthNvme);\r
+    }\r
+  }\r
+  if (EFI_ERROR (Status) || (DevInfo == NULL)) {\r
+    return;\r
+  }\r
+\r
+  for (DevInfoNvme = (OPAL_DEVICE_NVME *) DevInfo;\r
+       (UINTN) DevInfoNvme < ((UINTN) DevInfo + DevInfoLengthNvme);\r
+       DevInfoNvme = (OPAL_DEVICE_NVME *) ((UINTN) DevInfoNvme + DevInfoNvme->Length)) {\r
+    Bus = DevInfoNvme->Device.Bus;\r
+    Device = DevInfoNvme->Device.Device;\r
+    Function = DevInfoNvme->Device.Function;\r
+\r
+    RpBase    = 0;\r
+    NvmeCmdSt = 0;\r
+\r
+    ///\r
+    /// Save original RootPort configuration space to heap\r
+    ///\r
+    RpBase = SaveRestoreRootportConfSpace (\r
+                DevInfoNvme,\r
+                TRUE, // save\r
+                StorePcieConfDataList\r
+                );\r
+    MemoryBase = DevInfoNvme->BarAddr;\r
+    MemoryLength = 0;\r
+    ConfigureRootPortForPcieNand (RpBase, Bus, (UINT32) MemoryBase, (UINT32) MemoryLength);\r
+\r
+    ///\r
+    /// Enable PCIE decode for RootPort\r
+    ///\r
+    NvmeCmdSt = PciRead8 (RpBase + NVME_PCIE_PCICMD);\r
+    PciWrite8  (RpBase + NVME_PCIE_PCICMD,  0x6);\r
+\r
+    BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B));\r
+    SubClassCode  = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A));\r
+    ProgInt       = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x09));\r
+    if ((BaseClassCode != PCI_CLASS_MASS_STORAGE) ||\r
+        (SubClassCode != PCI_CLASS_MASS_STORAGE_NVM) ||\r
+        (ProgInt != PCI_IF_NVMHCI)) {\r
+      DEBUG ((DEBUG_ERROR, "%a() ClassCode/SubClassCode/PI are not supported\n", __FUNCTION__));\r
+    } else {\r
+      ZeroMem (&NvmeContext, sizeof (NVME_CONTEXT));\r
+      NvmeContext.Nbar = DevInfoNvme->BarAddr;\r
+      NvmeContext.PciBase = PCI_LIB_ADDRESS (Bus, Device, Function, 0x0);\r
+      NvmeContext.NvmeInitWaitTime = 0;\r
+      NvmeContext.Nsid = DevInfoNvme->NvmeNamespaceId;\r
+      NvmeAllocateResource (&NvmeContext);\r
+      Status = NvmeControllerInit (&NvmeContext);\r
+\r
+      OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE;\r
+      OpalDev.Sscp.ReceiveData = SecurityReceiveData;\r
+      OpalDev.Sscp.SendData = SecuritySendData;\r
+      OpalDev.DeviceType = OPAL_DEVICE_TYPE_NVME;\r
+      OpalDev.Device = (OPAL_DEVICE_COMMON *) DevInfoNvme;\r
+      OpalDev.Context = &NvmeContext;\r
+\r
+      UnlockOpalPassword (&OpalDev);\r
+\r
+      Status = NvmeControllerExit (&NvmeContext);\r
+      NvmeFreeResource (&NvmeContext);\r
+    }\r
+\r
+    ASSERT (RpBase != 0);\r
+    PciWrite8  (RpBase + NVME_PCIE_PCICMD, 0);\r
+    RpBase = SaveRestoreRootportConfSpace (\r
+                DevInfoNvme,\r
+                FALSE,  // restore\r
+                StorePcieConfDataList\r
+                );\r
+    PciWrite8  (RpBase + NVME_PCIE_PCICMD, NvmeCmdSt);\r
+  }\r
+\r
+  ZeroMem (DevInfo, DevInfoLengthNvme);\r
+  if ((UINTN) DevInfo != (UINTN) &TempDevInfoNvme) {\r
+    FreePages (DevInfo, EFI_SIZE_TO_PAGES (DevInfoLengthNvme));\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));\r
+}\r
+\r
+/**\r
+  Unlock OPAL password for S3.\r
+\r
+**/\r
+VOID\r
+OpalPasswordS3 (\r
+  VOID\r
+  )\r
+{\r
+  UnlockOpalPasswordAta ();\r
+  UnlockOpalPasswordNvme ();\r
+}\r
+\r
+/**\r
+  Entry point of the notification callback function itself within the PEIM.\r
+  It is to unlock OPAL password for S3.\r
+\r
+  @param  PeiServices      Indirect reference to the PEI Services Table.\r
+  @param  NotifyDescriptor Address of the notification descriptor data structure.\r
+  @param  Ppi              Address of the PPI that was installed.\r
+\r
+  @return Status of the notification.\r
+          The status code returned from this function is ignored.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+OpalPasswordEndOfPeiNotify(\r
+  IN EFI_PEI_SERVICES          **PeiServices,\r
+  IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,\r
+  IN VOID                      *Ppi\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_BOOT_MODE                     BootMode;\r
+\r
+  Status = PeiServicesGetBootMode (&BootMode);\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (BootMode != BOOT_ON_S3_RESUME) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__));\r
+\r
+  OpalPasswordS3 ();\r
+\r
+  DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_PEI_NOTIFY_DESCRIPTOR mOpalPasswordEndOfPeiNotifyDesc = {\r
+  (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+  &gEfiEndOfPeiSignalPpiGuid,\r
+  OpalPasswordEndOfPeiNotify\r
+};\r
+\r
+/**\r
+  Main entry for this module.\r
+\r
+  @param FileHandle             Handle of the file being invoked.\r
+  @param PeiServices            Pointer to PEI Services table.\r
+\r
+  @return Status from PeiServicesNotifyPpi.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+OpalPasswordPeiInit (\r
+  IN EFI_PEI_FILE_HANDLE        FileHandle,\r
+  IN CONST EFI_PEI_SERVICES     **PeiServices\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+\r
+  Status = PeiServicesNotifyPpi (&mOpalPasswordEndOfPeiNotifyDesc);\r
+  ASSERT_EFI_ERROR (Status);\r
+  return Status;\r
+}\r
+\r