mirror of https://github.com/acidanthera/audk.git
473 lines
19 KiB
C
473 lines
19 KiB
C
/** @file
|
|
Opal Password PEI driver which is used to unlock Opal Password for S3.
|
|
|
|
Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "OpalPasswordPei.h"
|
|
|
|
EFI_GUID mOpalDeviceLockBoxGuid = OPAL_DEVICE_LOCKBOX_GUID;
|
|
|
|
|
|
/**
|
|
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 PeiDev->SscPpi->ReceiveData (
|
|
PeiDev->SscPpi,
|
|
PeiDev->DeviceIndex,
|
|
SSC_PPI_GENERIC_TIMEOUT,
|
|
SecurityProtocolId,
|
|
SecurityProtocolSpecificData,
|
|
PayloadBufferSize,
|
|
PayloadBuffer,
|
|
PayloadTransferSize
|
|
);
|
|
}
|
|
|
|
/**
|
|
Send a security protocol command to a device.
|
|
|
|
The SendData function sends a security protocol command containing the payload
|
|
PayloadBuffer to the given MediaId. The security protocol command sent is
|
|
defined by SecurityProtocolId and contains the security protocol specific data
|
|
SecurityProtocolSpecificData. If the underlying protocol command requires a
|
|
specific padding for the command payload, the SendData function shall add padding
|
|
bytes to the command payload to satisfy the padding requirements.
|
|
|
|
For devices supporting the SCSI command set, the security protocol command is sent
|
|
using the SECURITY PROTOCOL OUT command defined in SPC-4.
|
|
|
|
For devices supporting the ATA command set, the security protocol command is sent
|
|
using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize
|
|
is non-zero. If the PayloadBufferSize is zero, the security protocol command is
|
|
sent using the Trusted Non-Data command defined in ATA8-ACS.
|
|
|
|
If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall
|
|
return EFI_INVALID_PARAMETER.
|
|
|
|
If the given MediaId does not support security protocol commands, the function
|
|
shall return EFI_UNSUPPORTED. If there is no media in the device, the function
|
|
returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the
|
|
device, the function returns EFI_MEDIA_CHANGED.
|
|
|
|
If the security protocol fails to complete within the Timeout period, the function
|
|
shall return EFI_TIMEOUT.
|
|
|
|
If the security protocol command completes without an error, the function shall return
|
|
EFI_SUCCESS. If the security protocol command completes with an error, the function
|
|
shall return EFI_DEVICE_ERROR.
|
|
|
|
@param This Indicates a pointer to the calling context.
|
|
@param MediaId ID of the medium to receive data from.
|
|
@param Timeout The timeout, in 100ns units, to use for the execution
|
|
of the security protocol command. A Timeout value of 0
|
|
means that this function will wait indefinitely for the
|
|
security protocol command to execute. If Timeout is greater
|
|
than zero, then this function will return EFI_TIMEOUT
|
|
if the time required to execute the 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 PeiDev->SscPpi->SendData (
|
|
PeiDev->SscPpi,
|
|
PeiDev->DeviceIndex,
|
|
SSC_PPI_GENERIC_TIMEOUT,
|
|
SecurityProtocolId,
|
|
SecurityProtocolSpecificData,
|
|
PayloadBufferSize,
|
|
PayloadBuffer
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
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) {
|
|
DEBUG ((DEBUG_INFO, "OpalPassword: S3 phase send BlockSid command to device!\n"));
|
|
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 the OPAL NVM Express and ATA devices for S3.
|
|
|
|
@param[in] SscPpi Pointer to the EDKII_PEI_STORAGE_SECURITY_CMD_PPI instance.
|
|
|
|
**/
|
|
VOID
|
|
UnlockOpalPasswordDevices (
|
|
IN EDKII_PEI_STORAGE_SECURITY_CMD_PPI *SscPpi
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *DevInfoBuffer;
|
|
UINT8 DummyData;
|
|
OPAL_DEVICE_LOCKBOX_DATA *DevInfo;
|
|
UINTN DevInfoLength;
|
|
EFI_DEVICE_PATH_PROTOCOL *SscDevicePath;
|
|
UINTN SscDevicePathLength;
|
|
UINTN SscDeviceNum;
|
|
UINTN SscDeviceIndex;
|
|
OPAL_PEI_DEVICE OpalDev;
|
|
|
|
//
|
|
// Get OPAL devices info from LockBox.
|
|
//
|
|
DevInfoBuffer = &DummyData;
|
|
DevInfoLength = sizeof (DummyData);
|
|
Status = RestoreLockBox (&mOpalDeviceLockBoxGuid, DevInfoBuffer, &DevInfoLength);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
DevInfoBuffer = AllocatePages (EFI_SIZE_TO_PAGES (DevInfoLength));
|
|
if (DevInfoBuffer != NULL) {
|
|
Status = RestoreLockBox (&mOpalDeviceLockBoxGuid, DevInfoBuffer, &DevInfoLength);
|
|
}
|
|
}
|
|
if (DevInfoBuffer == NULL || DevInfoBuffer == &DummyData) {
|
|
return;
|
|
} else if (EFI_ERROR (Status)) {
|
|
FreePages (DevInfoBuffer, EFI_SIZE_TO_PAGES (DevInfoLength));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Go through all the devices managed by the SSC PPI instance.
|
|
//
|
|
Status = SscPpi->GetNumberofDevices (SscPpi, &SscDeviceNum);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
for (SscDeviceIndex = 1; SscDeviceIndex <= SscDeviceNum; SscDeviceIndex++) {
|
|
Status = SscPpi->GetDevicePath (
|
|
SscPpi,
|
|
SscDeviceIndex,
|
|
&SscDevicePathLength,
|
|
&SscDevicePath
|
|
);
|
|
if (SscDevicePathLength <= sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
|
|
//
|
|
// Device path validity check.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Search the device in the restored LockBox.
|
|
//
|
|
for (DevInfo = (OPAL_DEVICE_LOCKBOX_DATA *) DevInfoBuffer;
|
|
(UINTN) DevInfo < ((UINTN) DevInfoBuffer + DevInfoLength);
|
|
DevInfo = (OPAL_DEVICE_LOCKBOX_DATA *) ((UINTN) DevInfo + DevInfo->Length)) {
|
|
//
|
|
// Find the matching device.
|
|
//
|
|
if ((DevInfo->DevicePathLength >= SscDevicePathLength) &&
|
|
(CompareMem (
|
|
DevInfo->DevicePath,
|
|
SscDevicePath,
|
|
SscDevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) == 0)) {
|
|
OpalDev.Signature = OPAL_PEI_DEVICE_SIGNATURE;
|
|
OpalDev.Sscp.ReceiveData = SecurityReceiveData;
|
|
OpalDev.Sscp.SendData = SecuritySendData;
|
|
OpalDev.Device = DevInfo;
|
|
OpalDev.Context = NULL;
|
|
OpalDev.SscPpi = SscPpi;
|
|
OpalDev.DeviceIndex = SscDeviceIndex;
|
|
UnlockOpalPassword (&OpalDev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
ZeroMem (DevInfoBuffer, DevInfoLength);
|
|
FreePages (DevInfoBuffer, EFI_SIZE_TO_PAGES (DevInfoLength));
|
|
|
|
}
|
|
|
|
/**
|
|
One notified function at the installation of EDKII_PEI_STORAGE_SECURITY_CMD_PPI.
|
|
It is to unlock OPAL password for S3.
|
|
|
|
@param[in] PeiServices Indirect reference to the PEI Services Table.
|
|
@param[in] NotifyDescriptor Address of the notification descriptor data structure.
|
|
@param[in] 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
|
|
OpalPasswordStorageSecurityPpiNotify (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
|
|
IN VOID *Ppi
|
|
)
|
|
{
|
|
DEBUG ((DEBUG_INFO, "%a entered at S3 resume!\n", __FUNCTION__));
|
|
|
|
UnlockOpalPasswordDevices ((EDKII_PEI_STORAGE_SECURITY_CMD_PPI *) Ppi);
|
|
|
|
DEBUG ((DEBUG_INFO, "%a exit at S3 resume!\n", __FUNCTION__));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
EFI_PEI_NOTIFY_DESCRIPTOR mOpalPasswordStorageSecurityPpiNotifyDesc = {
|
|
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
|
&gEdkiiPeiStorageSecurityCommandPpiGuid,
|
|
OpalPasswordStorageSecurityPpiNotify
|
|
};
|
|
|
|
|
|
/**
|
|
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;
|
|
EFI_BOOT_MODE BootMode;
|
|
|
|
Status = PeiServicesGetBootMode (&BootMode);
|
|
if ((EFI_ERROR (Status)) || (BootMode != BOOT_ON_S3_RESUME)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "%a: Enters in S3 path.\n", __FUNCTION__));
|
|
|
|
Status = PeiServicesNotifyPpi (&mOpalPasswordStorageSecurityPpiNotifyDesc);
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|