audk/SecurityPkg/HddPassword/HddPasswordPei.c

368 lines
12 KiB
C
Raw Normal View History

SecurityPkg/HddPassword: Add Security feature set support for ATA dev REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1529 This commit will add the 'Security feature set' support for ATA devices. According to the AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) specification, the Security feature set is an optional feature. In summary, the feature is a password system that restricts access to user data stored on an ATA device. A more detailed introduction of this feature can be referred from the ATA8-ACS spec. The HddPassword driver is composed of 2 parts: * A DXE driver and * A PEI driver The DXE driver consumes EFI_ATA_PASS_THRU_PROTOCOL instances and installs an HII GUI to manage the devices. If the managing device supports Security feature set, the HII page will provide the user with the ability to set/update/disable the password for this device. Also, if a password is being set via the Security feature set, a popup window will show during boot requesting the user to input password. Another feature supported by this driver is that for those managing devices with password set, they will be automatically unlocked during the S3 resume. This is done by the co-work of the DXE driver and the PEI driver: The DXE driver will save the password and the identification information for these devices into a LockBox, which is only allowed to restore during S3 resume. The PEI driver, during S3 resume, will restore the content in the LockBox and will consume EDKII_PEI_ATA_PASS_THRU_PPI instances to unlock devices. Cc: Chao Zhang <chao.b.zhang@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Eric Dong <eric.dong@intel.com>
2019-01-15 09:33:09 +01:00
/** @file
HddPassword PEI module which is used to unlock HDD password for S3.
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
SecurityPkg/HddPassword: Add Security feature set support for ATA dev REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1529 This commit will add the 'Security feature set' support for ATA devices. According to the AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) specification, the Security feature set is an optional feature. In summary, the feature is a password system that restricts access to user data stored on an ATA device. A more detailed introduction of this feature can be referred from the ATA8-ACS spec. The HddPassword driver is composed of 2 parts: * A DXE driver and * A PEI driver The DXE driver consumes EFI_ATA_PASS_THRU_PROTOCOL instances and installs an HII GUI to manage the devices. If the managing device supports Security feature set, the HII page will provide the user with the ability to set/update/disable the password for this device. Also, if a password is being set via the Security feature set, a popup window will show during boot requesting the user to input password. Another feature supported by this driver is that for those managing devices with password set, they will be automatically unlocked during the S3 resume. This is done by the co-work of the DXE driver and the PEI driver: The DXE driver will save the password and the identification information for these devices into a LockBox, which is only allowed to restore during S3 resume. The PEI driver, during S3 resume, will restore the content in the LockBox and will consume EDKII_PEI_ATA_PASS_THRU_PPI instances to unlock devices. Cc: Chao Zhang <chao.b.zhang@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Eric Dong <eric.dong@intel.com>
2019-01-15 09:33:09 +01:00
**/
#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;
}