audk/SecurityPkg/HddPassword/HddPasswordPei.c

375 lines
12 KiB
C

/** @file
HddPassword PEI module which is used to unlock HDD password for S3.
Copyright (c) 2019, 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 "HddPasswordPei.h"
EFI_GUID mHddPasswordDeviceInfoGuid = HDD_PASSWORD_DEVICE_INFO_GUID;
/**
Send unlock hdd password cmd through ATA PassThru PPI.
@param[in] AtaPassThru The pointer to the ATA PassThru PPI.
@param[in] Port The port number of the ATA device.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device.
@param[in] Identifier The identifier to set user or master password.
@param[in] Password The hdd password of attached ATA device.
@retval EFI_SUCCESS Successful to send unlock hdd password cmd.
@retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
@retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.
@retval EFI_DEVICE_ERROR Can not send unlock hdd password cmd.
**/
EFI_STATUS
UnlockDevice (
IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN CHAR8 Identifier,
IN CHAR8 *Password
)
{
EFI_STATUS Status;
EFI_ATA_COMMAND_BLOCK Acb;
EFI_ATA_STATUS_BLOCK *Asb;
EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;
UINT8 Buffer[HDD_PAYLOAD];
if ((AtaPassThru == NULL) || (Password == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in
// EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by
// the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,
// the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it
// may not be aligned when allocated on stack for some compilers. Hence, we
// use the API AllocateAlignedPages to ensure this structure is properly
// aligned.
//
Asb = AllocateAlignedPages (
EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),
AtaPassThru->Mode->IoAlign
);
if (Asb == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Prepare for ATA command block.
//
ZeroMem (&Acb, sizeof (Acb));
ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));
Acb.AtaCommand = ATA_SECURITY_UNLOCK_CMD;
Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));
//
// Prepare for ATA pass through packet.
//
ZeroMem (&Packet, sizeof (Packet));
Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT;
Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;
Packet.Asb = Asb;
Packet.Acb = &Acb;
((CHAR16 *) Buffer)[0] = Identifier & BIT0;
CopyMem (&((CHAR16 *) Buffer)[1], Password, HDD_PASSWORD_MAX_LENGTH);
Packet.OutDataBuffer = Buffer;
Packet.OutTransferLength = sizeof (Buffer);
Packet.Timeout = ATA_TIMEOUT;
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplierPort,
&Packet
);
if (!EFI_ERROR (Status) &&
((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&
((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) {
Status = EFI_DEVICE_ERROR;
}
FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
ZeroMem (Buffer, sizeof (Buffer));
DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));
return Status;
}
/**
Send security freeze lock cmd through ATA PassThru PPI.
@param[in] AtaPassThru The pointer to the ATA PassThru PPI.
@param[in] Port The port number of the ATA device.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device.
@retval EFI_SUCCESS Successful to send security freeze lock cmd.
@retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
@retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.
@retval EFI_DEVICE_ERROR Can not send security freeze lock cmd.
**/
EFI_STATUS
FreezeLockDevice (
IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort
)
{
EFI_STATUS Status;
EFI_ATA_COMMAND_BLOCK Acb;
EFI_ATA_STATUS_BLOCK *Asb;
EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;
if (AtaPassThru == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in
// EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by
// the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,
// the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it
// may not be aligned when allocated on stack for some compilers. Hence, we
// use the API AllocateAlignedPages to ensure this structure is properly
// aligned.
//
Asb = AllocateAlignedPages (
EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),
AtaPassThru->Mode->IoAlign
);
if (Asb == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Prepare for ATA command block.
//
ZeroMem (&Acb, sizeof (Acb));
ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));
Acb.AtaCommand = ATA_SECURITY_FREEZE_LOCK_CMD;
Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));
//
// Prepare for ATA pass through packet.
//
ZeroMem (&Packet, sizeof (Packet));
Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;
Packet.Length = EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER;
Packet.Asb = Asb;
Packet.Acb = &Acb;
Packet.Timeout = ATA_TIMEOUT;
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplierPort,
&Packet
);
if (!EFI_ERROR (Status) &&
((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&
((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) {
Status = EFI_DEVICE_ERROR;
}
FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));
return Status;
}
/**
Unlock HDD password for S3.
@param[in] AtaPassThruPpi Pointer to the EDKII_PEI_ATA_PASS_THRU_PPI instance.
**/
VOID
UnlockHddPassword (
IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThruPpi
)
{
EFI_STATUS Status;
VOID *Buffer;
UINTN Length;
UINT8 DummyData;
HDD_PASSWORD_DEVICE_INFO *DevInfo;
UINT16 Port;
UINT16 PortMultiplierPort;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN DevicePathLength;
//
// Get HDD password device info from LockBox.
//
Buffer = (VOID *) &DummyData;
Length = sizeof (DummyData);
Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);
if (Status == EFI_BUFFER_TOO_SMALL) {
Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Length));
if (Buffer != NULL) {
Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);
}
}
if ((Buffer == NULL) || (Buffer == (VOID *) &DummyData)) {
return;
} else if (EFI_ERROR (Status)) {
FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));
return;
}
Status = AtaPassThruPpi->GetDevicePath (AtaPassThruPpi, &DevicePathLength, &DevicePath);
if (EFI_ERROR (Status) || (DevicePathLength <= sizeof (EFI_DEVICE_PATH_PROTOCOL))) {
goto Exit;
}
//
// Go through all the devices managed by the AtaPassThru PPI instance.
//
Port = 0xFFFF;
while (TRUE) {
Status = AtaPassThruPpi->GetNextPort (AtaPassThruPpi, &Port);
if (EFI_ERROR (Status)) {
//
// We cannot find more legal port then we are done.
//
break;
}
PortMultiplierPort = 0xFFFF;
while (TRUE) {
Status = AtaPassThruPpi->GetNextDevice (AtaPassThruPpi, Port, &PortMultiplierPort);
if (EFI_ERROR (Status)) {
//
// We cannot find more legal port multiplier port number for ATA device
// on the port, then we are done.
//
break;
}
//
// Search the device in the restored LockBox.
//
DevInfo = (HDD_PASSWORD_DEVICE_INFO *) Buffer;
while ((UINTN) DevInfo < ((UINTN) Buffer + Length)) {
//
// Find the matching device.
//
if ((DevInfo->Device.Port == Port) &&
(DevInfo->Device.PortMultiplierPort == PortMultiplierPort) &&
(DevInfo->DevicePathLength >= DevicePathLength) &&
(CompareMem (
DevInfo->DevicePath,
DevicePath,
DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) == 0)) {
//
// If device locked, unlock first.
//
if (!IsZeroBuffer (DevInfo->Password, HDD_PASSWORD_MAX_LENGTH)) {
UnlockDevice (AtaPassThruPpi, Port, PortMultiplierPort, 0, DevInfo->Password);
}
//
// Freeze lock the device.
//
FreezeLockDevice (AtaPassThruPpi, Port, PortMultiplierPort);
break;
}
DevInfo = (HDD_PASSWORD_DEVICE_INFO *)
((UINTN) DevInfo + sizeof (HDD_PASSWORD_DEVICE_INFO) + DevInfo->DevicePathLength);
}
}
}
Exit:
ZeroMem (Buffer, Length);
FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));
}
/**
Entry point of the notification callback function itself within the PEIM.
It is to unlock HDD 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
HddPasswordAtaPassThruNotify (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
IN VOID *Ppi
)
{
DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__));
UnlockHddPassword ((EDKII_PEI_ATA_PASS_THRU_PPI *) Ppi);
DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__));
return EFI_SUCCESS;
}
EFI_PEI_NOTIFY_DESCRIPTOR mHddPasswordAtaPassThruPpiNotifyDesc = {
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEdkiiPeiAtaPassThruPpiGuid,
HddPasswordAtaPassThruNotify
};
/**
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
HddPasswordPeiInit (
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 (&mHddPasswordAtaPassThruPpiNotifyDesc);
ASSERT_EFI_ERROR (Status);
return Status;
}