--- /dev/null
+/** @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