summaryrefslogtreecommitdiffstats
path: root/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c
diff options
context:
space:
mode:
Diffstat (limited to 'SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c')
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c944
1 files changed, 944 insertions, 0 deletions
diff --git a/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c
new file mode 100644
index 0000000000..7f9e14fa81
--- /dev/null
+++ b/SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.c
@@ -0,0 +1,944 @@
+/** @file
+ Opal Password PEI driver which is used to unlock Opal Password for S3.
+
+Copyright (c) 2016 - 2018, 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 "OpalPasswordPei.h"
+
+EFI_GUID mOpalDeviceAtaGuid = OPAL_DEVICE_ATA_GUID;
+EFI_GUID mOpalDeviceNvmeGuid = OPAL_DEVICE_NVME_GUID;
+
+#define OPAL_PCIE_ROOTPORT_SAVESIZE (0x40)
+#define STORE_INVALID_ROOTPORT_INDEX ((UINT8) -1)
+
+/**
+ Get IOMMU PPI.
+
+ @return Pointer to IOMMU PPI.
+
+**/
+EDKII_IOMMU_PPI *
+GetIoMmu (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ IoMmu = NULL;
+ Status = PeiServicesLocatePpi (
+ &gEdkiiIoMmuPpiGuid,
+ 0,
+ NULL,
+ (VOID **) &IoMmu
+ );
+ if (!EFI_ERROR (Status) && (IoMmu != NULL)) {
+ return IoMmu;
+ }
+
+ return NULL;
+}
+
+/**
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+ OperationBusMasterCommonBuffer64 mapping.
+
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory address of the
+ allocated range.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumberOfBytes;
+ EFI_PHYSICAL_ADDRESS HostPhyAddress;
+ EDKII_IOMMU_PPI *IoMmu;
+
+ *HostAddress = NULL;
+ *DeviceAddress = 0;
+ *Mapping = NULL;
+
+ IoMmu = GetIoMmu ();
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->AllocateBuffer (
+ IoMmu,
+ EfiBootServicesData,
+ Pages,
+ HostAddress,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);
+ Status = IoMmu->Map (
+ IoMmu,
+ EdkiiIoMmuOperationBusMasterCommonBuffer,
+ *HostAddress,
+ &NumberOfBytes,
+ DeviceAddress,
+ Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);
+ *HostAddress = NULL;
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = IoMmu->SetAttribute (
+ IoMmu,
+ *Mapping,
+ EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
+ );
+ if (EFI_ERROR (Status)) {
+ IoMmu->Unmap (IoMmu, *Mapping);
+ IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);
+ *Mapping = NULL;
+ *HostAddress = NULL;
+ return Status;
+ }
+ } else {
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesData,
+ Pages,
+ &HostPhyAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ *HostAddress = (VOID *) (UINTN) HostPhyAddress;
+ *DeviceAddress = HostPhyAddress;
+ *Mapping = NULL;
+ }
+ return Status;
+}
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated range.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+IoMmuFreeBuffer (
+ IN UINTN Pages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ )
+{
+ EDKII_IOMMU_PPI *IoMmu;
+
+ IoMmu = GetIoMmu ();
+
+ if (IoMmu != NULL) {
+ IoMmu->SetAttribute (IoMmu, Mapping, 0);
+ IoMmu->Unmap (IoMmu, Mapping);
+ IoMmu->FreeBuffer (IoMmu, Pages, HostAddress);
+ } else {
+ PeiServicesFreePages (
+ (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress,
+ Pages
+ );
+ }
+}
+
+/**
+ Provide IO action support.
+
+ @param[in] PeiDev The opal device need to perform trusted 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 EFI_SUCCESS Perform the IO action success.
+ @retval Others Perform the IO action failed.
+
+**/
+EFI_STATUS
+PerformTrustedIo (
+ OPAL_PEI_DEVICE *PeiDev,
+ OPAL_IO_TYPE IoType,
+ UINT8 SecurityProtocol,
+ UINT16 SpSpecific,
+ UINTN TransferLength,
+ VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSizeBlocks;
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
+ OPAL_DEVICE_ATA *DevInfoAta;
+ AHCI_CONTEXT *AhciContext;
+ NVME_CONTEXT *NvmeContext;
+
+ Status = EFI_DEVICE_ERROR;
+ if (PeiDev->DeviceType == OPAL_DEVICE_TYPE_ATA) {
+ DevInfoAta = (OPAL_DEVICE_ATA *) PeiDev->Device;
+ AhciContext = (AHCI_CONTEXT *) PeiDev->Context;
+
+ 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( AhciContext->Buffer, HDD_PAYLOAD );
+ ASSERT( TransferLength <= HDD_PAYLOAD );
+
+ if (IoType == OpalSend) {
+ CopyMem( AhciContext->Buffer, Buffer, TransferLength );
+ }
+
+ Status = AhciPioTransfer(
+ AhciContext,
+ (UINT8) DevInfoAta->Port,
+ (UINT8) DevInfoAta->PortMultiplierPort,
+ NULL,
+ 0,
+ ( IoType == OpalSend ) ? FALSE : TRUE, // i/o direction
+ &AtaCommandBlock,
+ NULL,
+ AhciContext->Buffer,
+ (UINT32)TransferLength,
+ ATA_TIMEOUT
+ );
+
+ if (IoType == OpalRecv) {
+ CopyMem( Buffer, AhciContext->Buffer, TransferLength );
+ }
+ } else if (PeiDev->DeviceType == OPAL_DEVICE_TYPE_NVME) {
+ NvmeContext = (NVME_CONTEXT *) PeiDev->Context;
+ Status = NvmeSecuritySendReceive (
+ NvmeContext,
+ IoType == OpalSend,
+ SecurityProtocol,
+ SwapBytes16(SpSpecific),
+ TransferLength,
+ Buffer
+ );
+ } else {
+ DEBUG((DEBUG_ERROR, "DeviceType(%x) not support.\n", PeiDev->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_PEI_DEVICE *PeiDev;
+
+ PeiDev = OPAL_PEI_DEVICE_FROM_THIS (This);
+ if (PeiDev == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return PerformTrustedIo (
+ PeiDev,
+ 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_PEI_DEVICE *PeiDev;
+
+ PeiDev = OPAL_PEI_DEVICE_FROM_THIS (This);
+ if (PeiDev == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return PerformTrustedIo (
+ PeiDev,
+ OpalSend,
+ SecurityProtocolId,
+ SecurityProtocolSpecificData,
+ PayloadBufferSize,
+ PayloadBuffer
+ );
+
+}
+
+/**
+ Save/Restore RootPort configuration space.
+
+ @param[in] DevInfoNvme Pointer to NVMe device info.
+ @param[in] SaveAction TRUE: Save, FALSE: Restore
+ @param[in,out] PcieConfBufferList Configuration space data buffer for save/restore
+
+ @return PCIE base address of this RootPort
+**/
+UINTN
+SaveRestoreRootportConfSpace (
+ IN OPAL_DEVICE_NVME *DevInfoNvme,
+ IN BOOLEAN SaveAction,
+ IN OUT UINT8 **PcieConfBufferList
+ )
+{
+ UINTN RpBase;
+ UINTN Length;
+ OPAL_PCI_DEVICE *DevNode;
+ UINT8 *StorePcieConfData;
+ UINTN Index;
+
+ Length = 0;
+ Index = 0;
+ RpBase = 0;
+
+ while (sizeof (OPAL_DEVICE_NVME) + Length < DevInfoNvme->Length) {
+ DevNode = (OPAL_PCI_DEVICE *)((UINT8*)DevInfoNvme->PciBridgeNode + Length);
+ RpBase = PCI_LIB_ADDRESS (DevNode->Bus, DevNode->Device, DevNode->Function, 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 (OPAL_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
+}
+
+/**
+
+ 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.
+ @param[out] BlockSidSupported Whether device support BlockSid feature.
+
+**/
+BOOLEAN
+IsOpalDeviceLocked(
+ OPAL_PEI_DEVICE *OpalDev,
+ BOOLEAN *BlockSidSupported
+ )
+{
+ 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;
+ }
+
+ Session.OpalBaseComId = OpalBaseComId;
+ *BlockSidSupported = SupportedAttributes.BlockSid == 1 ? TRUE : FALSE;
+
+ Ret = OpalGetLockingInfo(&Session, &LockingFeature);
+ if (Ret != TcgResultSuccess) {
+ return FALSE;
+ }
+
+ return OpalDeviceLocked (&SupportedAttributes, &LockingFeature);
+}
+
+/**
+ Unlock OPAL password for S3.
+
+ @param[in] OpalDev Opal object to unlock.
+
+**/
+VOID
+UnlockOpalPassword (
+ IN OPAL_PEI_DEVICE *OpalDev
+ )
+{
+ TCG_RESULT Result;
+ OPAL_SESSION Session;
+ BOOLEAN BlockSidSupport;
+ UINT32 PpStorageFlags;
+ BOOLEAN BlockSIDEnabled;
+
+ BlockSidSupport = FALSE;
+ if (IsOpalDeviceLocked (OpalDev, &BlockSidSupport)) {
+ ZeroMem(&Session, sizeof (Session));
+ Session.Sscp = &OpalDev->Sscp;
+ Session.MediaId = 0;
+ Session.OpalBaseComId = OpalDev->Device->OpalBaseComId;
+
+ Result = OpalUtilUpdateGlobalLockingRange (
+ &Session,
+ OpalDev->Device->Password,
+ OpalDev->Device->PasswordLength,
+ FALSE,
+ FALSE
+ );
+ DEBUG ((
+ DEBUG_INFO,
+ "%a() OpalUtilUpdateGlobalLockingRange() Result = 0x%x\n",
+ __FUNCTION__,
+ Result
+ ));
+ }
+
+ PpStorageFlags = Tcg2PhysicalPresenceLibGetManagementFlags ();
+ if ((PpStorageFlags & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_ENABLE_BLOCK_SID) != 0) {
+ BlockSIDEnabled = TRUE;
+ } else {
+ BlockSIDEnabled = FALSE;
+ }
+ if (BlockSIDEnabled && BlockSidSupport) {
+ ZeroMem(&Session, sizeof (Session));
+ Session.Sscp = &OpalDev->Sscp;
+ Session.MediaId = 0;
+ Session.OpalBaseComId = OpalDev->Device->OpalBaseComId;
+ Result = OpalBlockSid (&Session, TRUE);
+ DEBUG ((
+ DEBUG_INFO,
+ "%a() OpalBlockSid() Result = 0x%x\n",
+ __FUNCTION__,
+ Result
+ ));
+ }
+}
+
+/**
+ Unlock ATA OPAL password for S3.
+
+**/
+VOID
+UnlockOpalPasswordAta (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *DevInfo;
+ OPAL_DEVICE_ATA TempDevInfoAta;
+ OPAL_DEVICE_ATA *DevInfoAta;
+ UINTN DevInfoLengthAta;
+ UINT8 Bus;
+ UINT8 Device;
+ UINT8 Function;
+ OPAL_PEI_DEVICE OpalDev;
+ UINT8 BaseClassCode;
+ UINT8 SubClassCode;
+ UINT8 SataCmdSt;
+ AHCI_CONTEXT AhciContext;
+ UINT32 AhciBar;
+
+ DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
+
+ //
+ // Get ATA OPAL device info from LockBox.
+ //
+ DevInfo = (UINT8 *) &TempDevInfoAta;
+ DevInfoLengthAta = sizeof (OPAL_DEVICE_ATA);
+ Status = RestoreLockBox (&mOpalDeviceAtaGuid, DevInfo, &DevInfoLengthAta);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DevInfo = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLengthAta));
+ if (DevInfo != NULL) {
+ Status = RestoreLockBox (&mOpalDeviceAtaGuid, DevInfo, &DevInfoLengthAta);
+ }
+ }
+ if (EFI_ERROR (Status) || (DevInfo == NULL)) {
+ return;
+ }
+
+ for (DevInfoAta = (OPAL_DEVICE_ATA *) DevInfo;
+ (UINTN) DevInfoAta < ((UINTN) DevInfo + DevInfoLengthAta);
+ DevInfoAta = (OPAL_DEVICE_ATA *) ((UINTN) DevInfoAta + DevInfoAta->Length)) {
+ Bus = DevInfoAta->Device.Bus;
+ Device = DevInfoAta->Device.Device;
+ Function = DevInfoAta->Device.Function;
+
+ SataCmdSt = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET));
+ PciWrite8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), 0x6);
+
+ BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B));
+ SubClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A));
+ if ((BaseClassCode != PCI_CLASS_MASS_STORAGE) ||
+ ((SubClassCode != PCI_CLASS_MASS_STORAGE_SATADPA) && (SubClassCode != PCI_CLASS_MASS_STORAGE_RAID))) {
+ DEBUG ((DEBUG_ERROR, "%a() ClassCode/SubClassCode are not supported\n", __FUNCTION__));
+ } else {
+ AhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
+ PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), DevInfoAta->BarAddr);
+
+ ZeroMem (&AhciContext, sizeof (AHCI_CONTEXT));
+ AhciContext.AhciBar = DevInfoAta->BarAddr;
+ AhciAllocateResource (&AhciContext);
+ Status = AhciModeInitialize (&AhciContext, (UINT8)DevInfoAta->Port);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a() AhciModeInitialize() error, Status: %r\n", __FUNCTION__, Status));
+ }
+
+ OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE;
+ OpalDev.Sscp.ReceiveData = SecurityReceiveData;
+ OpalDev.Sscp.SendData = SecuritySendData;
+ OpalDev.DeviceType = OPAL_DEVICE_TYPE_ATA;
+ OpalDev.Device = (OPAL_DEVICE_COMMON *) DevInfoAta;
+ OpalDev.Context = &AhciContext;
+
+ UnlockOpalPassword (&OpalDev);
+
+ AhciFreeResource (&AhciContext);
+ PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), AhciBar);
+ }
+ PciWrite8 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), SataCmdSt);
+ }
+
+ ZeroMem (DevInfo, DevInfoLengthAta);
+ if ((UINTN) DevInfo != (UINTN) &TempDevInfoAta) {
+ FreePages (DevInfo, EFI_SIZE_TO_PAGES (DevInfoLengthAta));
+ }
+
+ DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
+}
+
+/**
+ Unlock NVMe OPAL password for S3.
+
+**/
+VOID
+UnlockOpalPasswordNvme (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *DevInfo;
+ OPAL_DEVICE_NVME TempDevInfoNvme;
+ OPAL_DEVICE_NVME *DevInfoNvme;
+ UINTN DevInfoLengthNvme;
+ UINT8 Bus;
+ UINT8 Device;
+ UINT8 Function;
+ OPAL_PEI_DEVICE OpalDev;
+ UINT8 BaseClassCode;
+ UINT8 SubClassCode;
+ UINT8 ProgInt;
+ UINT8 NvmeCmdSt;
+ UINT8 *StorePcieConfDataList[16];
+ UINTN RpBase;
+ UINTN MemoryBase;
+ UINTN MemoryLength;
+ NVME_CONTEXT NvmeContext;
+
+ DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
+
+ //
+ // Get NVMe OPAL device info from LockBox.
+ //
+ DevInfo = (UINT8 *) &TempDevInfoNvme;
+ DevInfoLengthNvme = sizeof (OPAL_DEVICE_NVME);
+ Status = RestoreLockBox (&mOpalDeviceNvmeGuid, DevInfo, &DevInfoLengthNvme);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DevInfo = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLengthNvme));
+ if (DevInfo != NULL) {
+ Status = RestoreLockBox (&mOpalDeviceNvmeGuid, DevInfo, &DevInfoLengthNvme);
+ }
+ }
+ if (EFI_ERROR (Status) || (DevInfo == NULL)) {
+ return;
+ }
+
+ for (DevInfoNvme = (OPAL_DEVICE_NVME *) DevInfo;
+ (UINTN) DevInfoNvme < ((UINTN) DevInfo + DevInfoLengthNvme);
+ DevInfoNvme = (OPAL_DEVICE_NVME *) ((UINTN) DevInfoNvme + DevInfoNvme->Length)) {
+ Bus = DevInfoNvme->Device.Bus;
+ Device = DevInfoNvme->Device.Device;
+ Function = DevInfoNvme->Device.Function;
+
+ RpBase = 0;
+ NvmeCmdSt = 0;
+
+ ///
+ /// Save original RootPort configuration space to heap
+ ///
+ RpBase = SaveRestoreRootportConfSpace (
+ DevInfoNvme,
+ TRUE, // save
+ StorePcieConfDataList
+ );
+ MemoryBase = DevInfoNvme->BarAddr;
+ MemoryLength = 0;
+ ConfigureRootPortForPcieNand (RpBase, Bus, (UINT32) MemoryBase, (UINT32) MemoryLength);
+
+ ///
+ /// Enable PCIE decode for RootPort
+ ///
+ NvmeCmdSt = PciRead8 (RpBase + NVME_PCIE_PCICMD);
+ PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0x6);
+
+ BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B));
+ SubClassCode = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A));
+ ProgInt = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x09));
+ if ((BaseClassCode != PCI_CLASS_MASS_STORAGE) ||
+ (SubClassCode != PCI_CLASS_MASS_STORAGE_NVM) ||
+ (ProgInt != PCI_IF_NVMHCI)) {
+ DEBUG ((DEBUG_ERROR, "%a() ClassCode/SubClassCode/PI are not supported\n", __FUNCTION__));
+ } else {
+ ZeroMem (&NvmeContext, sizeof (NVME_CONTEXT));
+ NvmeContext.Nbar = DevInfoNvme->BarAddr;
+ NvmeContext.PciBase = PCI_LIB_ADDRESS (Bus, Device, Function, 0x0);
+ NvmeContext.NvmeInitWaitTime = 0;
+ NvmeContext.Nsid = DevInfoNvme->NvmeNamespaceId;
+ NvmeAllocateResource (&NvmeContext);
+ Status = NvmeControllerInit (&NvmeContext);
+
+ OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE;
+ OpalDev.Sscp.ReceiveData = SecurityReceiveData;
+ OpalDev.Sscp.SendData = SecuritySendData;
+ OpalDev.DeviceType = OPAL_DEVICE_TYPE_NVME;
+ OpalDev.Device = (OPAL_DEVICE_COMMON *) DevInfoNvme;
+ OpalDev.Context = &NvmeContext;
+
+ UnlockOpalPassword (&OpalDev);
+
+ Status = NvmeControllerExit (&NvmeContext);
+ NvmeFreeResource (&NvmeContext);
+ }
+
+ ASSERT (RpBase != 0);
+ PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0);
+ RpBase = SaveRestoreRootportConfSpace (
+ DevInfoNvme,
+ FALSE, // restore
+ StorePcieConfDataList
+ );
+ PciWrite8 (RpBase + NVME_PCIE_PCICMD, NvmeCmdSt);
+ }
+
+ ZeroMem (DevInfo, DevInfoLengthNvme);
+ if ((UINTN) DevInfo != (UINTN) &TempDevInfoNvme) {
+ FreePages (DevInfo, EFI_SIZE_TO_PAGES (DevInfoLengthNvme));
+ }
+
+ DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
+}
+
+/**
+ Unlock OPAL password for S3.
+
+**/
+VOID
+OpalPasswordS3 (
+ VOID
+ )
+{
+ UnlockOpalPasswordAta ();
+ UnlockOpalPasswordNvme ();
+}
+
+/**
+ Entry point of the notification callback function itself within the PEIM.
+ It is to unlock OPAL password for S3.
+
+ @param PeiServices Indirect reference to the PEI Services Table.
+ @param NotifyDescriptor Address of the notification descriptor data structure.
+ @param Ppi Address of the PPI that was installed.
+
+ @return Status of the notification.
+ The status code returned from this function is ignored.
+**/
+EFI_STATUS
+EFIAPI
+OpalPasswordEndOfPeiNotify(
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
+ IN VOID *Ppi
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MODE BootMode;
+
+ Status = PeiServicesGetBootMode (&BootMode);
+ ASSERT_EFI_ERROR (Status);
+ if (BootMode != BOOT_ON_S3_RESUME) {
+ return EFI_UNSUPPORTED;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__));
+
+ OpalPasswordS3 ();
+
+ DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__));
+
+ return EFI_SUCCESS;
+}
+
+EFI_PEI_NOTIFY_DESCRIPTOR mOpalPasswordEndOfPeiNotifyDesc = {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiEndOfPeiSignalPpiGuid,
+ OpalPasswordEndOfPeiNotify
+};
+
+/**
+ Main entry for this module.
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Pointer to PEI Services table.
+
+ @return Status from PeiServicesNotifyPpi.
+
+**/
+EFI_STATUS
+EFIAPI
+OpalPasswordPeiInit (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+
+ Status = PeiServicesNotifyPpi (&mOpalPasswordEndOfPeiNotifyDesc);
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+