audk/SecurityPkg/HddPassword/HddPasswordDxe.c

2824 lines
99 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
HDD password driver which is used to support HDD security feature.
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 "HddPasswordDxe.h"
EFI_GUID mHddPasswordVendorGuid = HDD_PASSWORD_CONFIG_GUID;
CHAR16 mHddPasswordVendorStorageName[] = L"HDD_PASSWORD_CONFIG";
LIST_ENTRY mHddPasswordConfigFormList;
UINT32 mNumberOfHddDevices = 0;
EFI_GUID mHddPasswordDeviceInfoGuid = HDD_PASSWORD_DEVICE_INFO_GUID;
BOOLEAN mHddPasswordEndOfDxe = FALSE;
HDD_PASSWORD_REQUEST_VARIABLE *mHddPasswordRequestVariable = NULL;
UINTN mHddPasswordRequestVariableSize = 0;
HII_VENDOR_DEVICE_PATH mHddPasswordHiiVendorDevicePath = {
{
{
HARDWARE_DEVICE_PATH,
HW_VENDOR_DP,
{
(UINT8) (sizeof (VENDOR_DEVICE_PATH)),
(UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
}
},
HDD_PASSWORD_CONFIG_GUID
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
(UINT8) (END_DEVICE_PATH_LENGTH),
(UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
}
}
};
/**
Check if the password is full zero.
@param[in] Password Points to the data buffer
@retval TRUE This password string is full zero.
@retval FALSE This password string is not full zero.
**/
BOOLEAN
PasswordIsFullZero (
IN CHAR8 *Password
)
{
UINTN Index;
for (Index = 0; Index < HDD_PASSWORD_MAX_LENGTH; Index++) {
if (Password[Index] != 0) {
return FALSE;
}
}
return TRUE;
}
/**
Save device info.
@param[in] ConfigFormEntry Points to HDD_PASSWORD_CONFIG_FORM_ENTRY buffer
@param[in,out] TempDevInfo Points to HDD_PASSWORD_DEVICE_INFO buffer
**/
VOID
SaveDeviceInfo (
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry,
IN OUT HDD_PASSWORD_DEVICE_INFO *TempDevInfo
)
{
TempDevInfo->Device.Bus = (UINT8) ConfigFormEntry->Bus;
TempDevInfo->Device.Device = (UINT8) ConfigFormEntry->Device;
TempDevInfo->Device.Function = (UINT8) ConfigFormEntry->Function;
TempDevInfo->Device.Port = ConfigFormEntry->Port;
TempDevInfo->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;
CopyMem (TempDevInfo->Password, ConfigFormEntry->Password, HDD_PASSWORD_MAX_LENGTH);
TempDevInfo->DevicePathLength = (UINT32) GetDevicePathSize (ConfigFormEntry->DevicePath);
CopyMem (TempDevInfo->DevicePath, ConfigFormEntry->DevicePath, TempDevInfo->DevicePathLength);
}
/**
Build HDD password device info and save them to LockBox.
**/
VOID
BuildHddPasswordDeviceInfo (
VOID
)
{
EFI_STATUS Status;
LIST_ENTRY *Entry;
HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry;
HDD_PASSWORD_DEVICE_INFO *DevInfo;
HDD_PASSWORD_DEVICE_INFO *TempDevInfo;
UINTN DevInfoLength;
UINT8 DummyData;
BOOLEAN S3InitDevicesExist;
UINTN S3InitDevicesLength;
EFI_DEVICE_PATH_PROTOCOL *S3InitDevices;
EFI_DEVICE_PATH_PROTOCOL *S3InitDevicesBak;
//
// Build HDD password device info and save them to LockBox.
//
DevInfoLength = 0;
EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {
ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);
//
// 1. Handle device which already set password.
// 2. When request to send freeze command, driver also needs to handle device
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
// which support security feature.
//
if ((!PasswordIsFullZero (ConfigFormEntry->Password)) ||
((ConfigFormEntry->IfrData.SecurityStatus.Supported != 0) &&
(ConfigFormEntry->IfrData.SecurityStatus.Enabled == 0))) {
DevInfoLength += sizeof (HDD_PASSWORD_DEVICE_INFO) +
GetDevicePathSize (ConfigFormEntry->DevicePath);
}
}
if (DevInfoLength == 0) {
return;
}
S3InitDevicesLength = sizeof (DummyData);
Status = RestoreLockBox (
&gS3StorageDeviceInitListGuid,
&DummyData,
&S3InitDevicesLength
);
ASSERT ((Status == EFI_NOT_FOUND) || (Status == EFI_BUFFER_TOO_SMALL));
if (Status == EFI_NOT_FOUND) {
S3InitDevices = NULL;
S3InitDevicesExist = FALSE;
} else if (Status == EFI_BUFFER_TOO_SMALL) {
S3InitDevices = AllocatePool (S3InitDevicesLength);
ASSERT (S3InitDevices != NULL);
Status = RestoreLockBox (
&gS3StorageDeviceInitListGuid,
S3InitDevices,
&S3InitDevicesLength
);
ASSERT_EFI_ERROR (Status);
S3InitDevicesExist = TRUE;
} else {
return;
}
DevInfo = AllocateZeroPool (DevInfoLength);
ASSERT (DevInfo != NULL);
TempDevInfo = DevInfo;
EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {
ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);
if ((!PasswordIsFullZero (ConfigFormEntry->Password)) ||
((ConfigFormEntry->IfrData.SecurityStatus.Supported != 0) &&
(ConfigFormEntry->IfrData.SecurityStatus.Enabled == 0))) {
SaveDeviceInfo (ConfigFormEntry, TempDevInfo);
S3InitDevicesBak = S3InitDevices;
S3InitDevices = AppendDevicePathInstance (
S3InitDevicesBak,
ConfigFormEntry->DevicePath
);
if (S3InitDevicesBak != NULL) {
FreePool (S3InitDevicesBak);
}
ASSERT (S3InitDevices != NULL);
TempDevInfo = (HDD_PASSWORD_DEVICE_INFO *) ((UINTN)TempDevInfo +
sizeof (HDD_PASSWORD_DEVICE_INFO) +
TempDevInfo->DevicePathLength);
}
}
Status = SaveLockBox (
&mHddPasswordDeviceInfoGuid,
DevInfo,
DevInfoLength
);
ASSERT_EFI_ERROR (Status);
Status = SetLockBoxAttributes (
&mHddPasswordDeviceInfoGuid,
LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY
);
ASSERT_EFI_ERROR (Status);
S3InitDevicesLength = GetDevicePathSize (S3InitDevices);
if (S3InitDevicesExist) {
Status = UpdateLockBox (
&gS3StorageDeviceInitListGuid,
0,
S3InitDevices,
S3InitDevicesLength
);
ASSERT_EFI_ERROR (Status);
} else {
Status = SaveLockBox (
&gS3StorageDeviceInitListGuid,
S3InitDevices,
S3InitDevicesLength
);
ASSERT_EFI_ERROR (Status);
Status = SetLockBoxAttributes (
&gS3StorageDeviceInitListGuid,
LOCK_BOX_ATTRIBUTE_RESTORE_IN_S3_ONLY
);
ASSERT_EFI_ERROR (Status);
}
ZeroMem (DevInfo, DevInfoLength);
FreePool (DevInfo);
FreePool (S3InitDevices);
}
/**
Send freeze lock cmd through Ata Pass Thru Protocol.
@param[in] AtaPassThru The pointer to the ATA_PASS_THRU protocol.
@param[in] Port The port number of the ATA device to send the command.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command.
If there is no port multiplier, then specify 0xFFFF.
@retval EFI_SUCCESS Successful to send freeze lock cmd.
@retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
@retval EFI_OUT_OF_RESOURCES Not enough memory to send freeze lock cmd.
@retval EFI_DEVICE_ERROR Can not send freeze lock cmd.
**/
EFI_STATUS
FreezeLockDevice (
IN EFI_ATA_PASS_THRU_PROTOCOL *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,
NULL
);
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;
}
/**
Get attached harddisk identify data through Ata Pass Thru Protocol.
@param[in] AtaPassThru The pointer to the ATA_PASS_THRU protocol.
@param[in] Port The port number of the ATA device to send the command.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command.
If there is no port multiplier, then specify 0xFFFF.
@param[in] IdentifyData The buffer to store identify data.
@retval EFI_SUCCESS Successful to get identify data.
@retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
@retval EFI_OUT_OF_RESOURCES Not enough memory to get identify data.
@retval EFI_DEVICE_ERROR Can not get identify data.
**/
EFI_STATUS
GetHddDeviceIdentifyData (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN ATA_IDENTIFY_DATA *IdentifyData
)
{
EFI_STATUS Status;
EFI_ATA_COMMAND_BLOCK Acb;
EFI_ATA_STATUS_BLOCK *Asb;
EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;
if ((AtaPassThru == NULL) || (IdentifyData == 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_CMD_IDENTIFY_DRIVE;
Acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4)));
//
// Prepare for ATA pass through packet.
//
ZeroMem (&Packet, sizeof (Packet));
Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN;
Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;
Packet.Asb = Asb;
Packet.Acb = &Acb;
Packet.InDataBuffer = IdentifyData;
Packet.InTransferLength = sizeof (ATA_IDENTIFY_DATA);
Packet.Timeout = ATA_TIMEOUT;
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplierPort,
&Packet,
NULL
);
FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
return Status;
}
/**
Parse security status according to identify data.
@param[in] IdentifyData The buffer to store identify data.
@param[in, out] IfrData IFR data to hold security status.
**/
VOID
GetHddPasswordSecurityStatus (
IN ATA_IDENTIFY_DATA *IdentifyData,
IN OUT HDD_PASSWORD_CONFIG *IfrData
)
{
IfrData->SecurityStatus.Supported = (IdentifyData->command_set_supported_82 & BIT1) ? 1 : 0;
IfrData->SecurityStatus.Enabled = (IdentifyData->security_status & BIT1) ? 1 : 0;
IfrData->SecurityStatus.Locked = (IdentifyData->security_status & BIT2) ? 1 : 0;
IfrData->SecurityStatus.Frozen = (IdentifyData->security_status & BIT3) ? 1 : 0;
IfrData->SecurityStatus.UserPasswordStatus = IfrData->SecurityStatus.Enabled;
IfrData->SecurityStatus.MasterPasswordStatus = IfrData->SecurityStatus.Supported;
DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Supported = %x\n", IfrData->SecurityStatus.Supported));
DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Enabled = %x\n", IfrData->SecurityStatus.Enabled));
DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Locked = %x\n", IfrData->SecurityStatus.Locked));
DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.Frozen = %x\n", IfrData->SecurityStatus.Frozen));
DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.UserPasswordStatus = %x\n", IfrData->SecurityStatus.UserPasswordStatus));
DEBUG ((DEBUG_INFO, "IfrData->SecurityStatus.MasterPasswordStatus = %x\n", IfrData->SecurityStatus.MasterPasswordStatus));
}
/**
Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context.
**/
VOID
EFIAPI
HddPasswordEndOfDxeEventNotify (
EFI_EVENT Event,
VOID *Context
)
{
LIST_ENTRY *Entry;
HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry;
EFI_STATUS Status;
ATA_IDENTIFY_DATA IdentifyData;
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
mHddPasswordEndOfDxe = TRUE;
if (mHddPasswordRequestVariable != NULL) {
//
// Free the HDD password request variable buffer here
// as the HDD password requests should have been processed.
//
FreePool (mHddPasswordRequestVariable);
mHddPasswordRequestVariable = NULL;
mHddPasswordRequestVariableSize = 0;
}
//
// If no any device, return directly.
//
if (IsListEmpty (&mHddPasswordConfigFormList)) {
gBS->CloseEvent (Event);
return;
}
BuildHddPasswordDeviceInfo ();
//
// Zero passsword and freeze lock device.
//
EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {
ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);
ZeroMem (ConfigFormEntry->Password, HDD_PASSWORD_MAX_LENGTH);
//
// Check whether need send freeze lock command.
// Below device will be froze:
// 1. Device not enable password.
// 2. Device enable password and unlocked.
//
if ((ConfigFormEntry->IfrData.SecurityStatus.Supported != 0) &&
(ConfigFormEntry->IfrData.SecurityStatus.Locked == 0) &&
(ConfigFormEntry->IfrData.SecurityStatus.Frozen == 0)) {
Status = FreezeLockDevice (ConfigFormEntry->AtaPassThru, ConfigFormEntry->Port, ConfigFormEntry->PortMultiplierPort);
DEBUG ((DEBUG_INFO, "FreezeLockDevice return %r!\n", Status));
Status = GetHddDeviceIdentifyData (
ConfigFormEntry->AtaPassThru,
ConfigFormEntry->Port,
ConfigFormEntry->PortMultiplierPort,
&IdentifyData
);
GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);
}
}
DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
gBS->CloseEvent (Event);
}
/**
Generate Salt value.
@param[in, out] SaltValue Points to the salt buffer, 32 bytes
**/
VOID
GenSalt (
IN OUT UINT8 *SaltValue
)
{
RandomSeed (NULL, 0);
RandomBytes (SaltValue, PASSWORD_SALT_SIZE);
}
/**
Hash the data to get credential.
@param[in] Buffer Points to the data buffer
@param[in] BufferSize Buffer size
@param[in] SaltValue Points to the salt buffer, 32 bytes
@param[out] Credential Points to the hashed result
@retval TRUE Hash the data successfully.
@retval FALSE Failed to hash the data.
**/
BOOLEAN
GenerateCredential (
IN UINT8 *Buffer,
IN UINTN BufferSize,
IN UINT8 *SaltValue,
OUT UINT8 *Credential
)
{
BOOLEAN Status;
UINTN HashSize;
VOID *Hash;
VOID *HashData;
Hash = NULL;
HashData = NULL;
Status = FALSE;
HashSize = Sha256GetContextSize ();
Hash = AllocateZeroPool (HashSize);
ASSERT (Hash != NULL);
if (Hash == NULL) {
goto Done;
}
Status = Sha256Init (Hash);
if (!Status) {
goto Done;
}
HashData = AllocateZeroPool (PASSWORD_SALT_SIZE + BufferSize);
ASSERT (HashData != NULL);
if (HashData == NULL) {
goto Done;
}
CopyMem (HashData, SaltValue, PASSWORD_SALT_SIZE);
CopyMem ((UINT8 *) HashData + PASSWORD_SALT_SIZE, Buffer, BufferSize);
Status = Sha256Update (Hash, HashData, PASSWORD_SALT_SIZE + BufferSize);
if (!Status) {
goto Done;
}
Status = Sha256Final (Hash, Credential);
Done:
if (Hash != NULL) {
FreePool (Hash);
}
if (HashData != NULL) {
ZeroMem (HashData, PASSWORD_SALT_SIZE + BufferSize);
FreePool (HashData);
}
return Status;
}
/**
Save HDD password variable that will be used to validate HDD password
when the device is at frozen state.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
@param[in] Password The hdd password of attached ATA device.
**/
VOID
SaveHddPasswordVariable (
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry,
IN CHAR8 *Password
)
{
EFI_STATUS Status;
HDD_PASSWORD_VARIABLE *TempVariable;
UINTN TempVariableSize;
HDD_PASSWORD_VARIABLE *NextNode;
HDD_PASSWORD_VARIABLE *Variable;
UINTN VariableSize;
HDD_PASSWORD_VARIABLE *NewVariable;
UINTN NewVariableSize;
BOOLEAN Delete;
BOOLEAN HashOk;
UINT8 HashData[SHA256_DIGEST_SIZE];
UINT8 SaltData[PASSWORD_SALT_SIZE];
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
Delete = FALSE;
if (!PasswordIsFullZero (Password)) {
//
// It is Set/Update HDD Password.
//
ZeroMem (HashData, sizeof (HashData));
ZeroMem (SaltData, sizeof (SaltData));
GenSalt (SaltData);
HashOk = GenerateCredential ((UINT8 *) Password, HDD_PASSWORD_MAX_LENGTH, SaltData, HashData);
if (!HashOk) {
DEBUG ((DEBUG_INFO, "GenerateCredential failed\n"));
return;
}
} else {
//
// It is Disable HDD Password.
// Go to delete the variable node for the HDD password device.
//
Delete = TRUE;
}
Variable = NULL;
VariableSize = 0;
NewVariable = NULL;
NewVariableSize = 0;
Status = GetVariable2 (
HDD_PASSWORD_VARIABLE_NAME,
&mHddPasswordVendorGuid,
(VOID **) &Variable,
&VariableSize
);
if (Delete) {
if (!EFI_ERROR (Status) && (Variable != NULL)) {
TempVariable = Variable;
TempVariableSize = VariableSize;
while (TempVariableSize >= sizeof (HDD_PASSWORD_VARIABLE)) {
if ((TempVariable->Device.Bus == ConfigFormEntry->Bus) &&
(TempVariable->Device.Device == ConfigFormEntry->Device) &&
(TempVariable->Device.Function == ConfigFormEntry->Function) &&
(TempVariable->Device.Port == ConfigFormEntry->Port) &&
(TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {
//
// Found the node for the HDD password device.
// Delete the node.
//
NextNode = TempVariable + 1;
CopyMem (TempVariable, NextNode, (UINTN) Variable + VariableSize - (UINTN) NextNode);
NewVariable = Variable;
NewVariableSize = VariableSize - sizeof (HDD_PASSWORD_VARIABLE);
break;
}
TempVariableSize -= sizeof (HDD_PASSWORD_VARIABLE);
TempVariable += 1;
}
if (NewVariable == NULL) {
DEBUG ((DEBUG_INFO, "The variable node for the HDD password device is not found\n"));
}
} else {
DEBUG ((DEBUG_INFO, "HddPassword variable get failed (%r)\n", Status));
}
} else {
if (!EFI_ERROR (Status) && (Variable != NULL)) {
TempVariable = Variable;
TempVariableSize = VariableSize;
while (TempVariableSize >= sizeof (HDD_PASSWORD_VARIABLE)) {
if ((TempVariable->Device.Bus == ConfigFormEntry->Bus) &&
(TempVariable->Device.Device == ConfigFormEntry->Device) &&
(TempVariable->Device.Function == ConfigFormEntry->Function) &&
(TempVariable->Device.Port == ConfigFormEntry->Port) &&
(TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {
//
// Found the node for the HDD password device.
// Update the node.
//
CopyMem (TempVariable->PasswordHash, HashData, sizeof (HashData));
CopyMem (TempVariable->PasswordSalt, SaltData, sizeof (SaltData));
NewVariable = Variable;
NewVariableSize = VariableSize;
break;
}
TempVariableSize -= sizeof (HDD_PASSWORD_VARIABLE);
TempVariable += 1;
}
if (NewVariable == NULL) {
//
// The node for the HDD password device is not found.
// Create node for the HDD password device.
//
NewVariableSize = VariableSize + sizeof (HDD_PASSWORD_VARIABLE);
NewVariable = AllocateZeroPool (NewVariableSize);
ASSERT (NewVariable != NULL);
CopyMem (NewVariable, Variable, VariableSize);
TempVariable = (HDD_PASSWORD_VARIABLE *) ((UINTN) NewVariable + VariableSize);
TempVariable->Device.Bus = (UINT8) ConfigFormEntry->Bus;
TempVariable->Device.Device = (UINT8) ConfigFormEntry->Device;
TempVariable->Device.Function = (UINT8) ConfigFormEntry->Function;
TempVariable->Device.Port = ConfigFormEntry->Port;
TempVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;
CopyMem (TempVariable->PasswordHash, HashData, sizeof (HashData));
CopyMem (TempVariable->PasswordSalt, SaltData, sizeof (SaltData));
}
} else {
NewVariableSize = sizeof (HDD_PASSWORD_VARIABLE);
NewVariable = AllocateZeroPool (NewVariableSize);
ASSERT (NewVariable != NULL);
NewVariable->Device.Bus = (UINT8) ConfigFormEntry->Bus;
NewVariable->Device.Device = (UINT8) ConfigFormEntry->Device;
NewVariable->Device.Function = (UINT8) ConfigFormEntry->Function;
NewVariable->Device.Port = ConfigFormEntry->Port;
NewVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;
CopyMem (NewVariable->PasswordHash, HashData, sizeof (HashData));
CopyMem (NewVariable->PasswordSalt, SaltData, sizeof (SaltData));
}
}
if (NewVariable != NULL) {
Status = gRT->SetVariable (
HDD_PASSWORD_VARIABLE_NAME,
&mHddPasswordVendorGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
NewVariableSize,
NewVariable
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "HddPassword variable set failed (%r)\n", Status));
}
}
if (NewVariable != Variable) {
FreePool (NewVariable);
}
if (Variable != NULL) {
FreePool (Variable);
}
DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
}
/**
Get saved HDD password variable that will be used to validate HDD password
when the device is at frozen state.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
@param[out] HddPasswordVariable The variable node for the HDD password device.
@retval TRUE The variable node for the HDD password device is found and returned.
@retval FALSE The variable node for the HDD password device is not found.
**/
BOOLEAN
GetSavedHddPasswordVariable (
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry,
OUT HDD_PASSWORD_VARIABLE *HddPasswordVariable
)
{
EFI_STATUS Status;
HDD_PASSWORD_VARIABLE *TempVariable;
HDD_PASSWORD_VARIABLE *Variable;
UINTN VariableSize;
BOOLEAN Found;
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
Variable = NULL;
VariableSize = 0;
Status = GetVariable2 (
HDD_PASSWORD_VARIABLE_NAME,
&mHddPasswordVendorGuid,
(VOID **) &Variable,
&VariableSize
);
if (EFI_ERROR (Status) || (Variable == NULL)) {
DEBUG ((DEBUG_INFO, "HddPassword variable get failed (%r)\n", Status));
return FALSE;
}
Found = FALSE;
TempVariable = Variable;
while (VariableSize >= sizeof (HDD_PASSWORD_VARIABLE)) {
if ((TempVariable->Device.Bus == ConfigFormEntry->Bus) &&
(TempVariable->Device.Device == ConfigFormEntry->Device) &&
(TempVariable->Device.Function == ConfigFormEntry->Function) &&
(TempVariable->Device.Port == ConfigFormEntry->Port) &&
(TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {
//
// Found the node for the HDD password device.
// Get the node.
//
CopyMem (HddPasswordVariable, TempVariable, sizeof (HDD_PASSWORD_VARIABLE));
Found = TRUE;
break;
}
VariableSize -= sizeof (HDD_PASSWORD_VARIABLE);
TempVariable += 1;
}
FreePool (Variable);
if (!Found) {
DEBUG ((DEBUG_INFO, "The variable node for the HDD password device is not found\n"));
}
DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
return Found;
}
/**
Use saved HDD password variable to validate HDD password
when the device is at frozen state.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
@param[in] Password The hdd password of attached ATA device.
@retval EFI_SUCCESS Pass to validate the HDD password.
@retval EFI_NOT_FOUND The variable node for the HDD password device is not found.
@retval EFI_DEVICE_ERROR Failed to generate credential for the HDD password.
@retval EFI_INVALID_PARAMETER Failed to validate the HDD password.
**/
EFI_STATUS
ValidateHddPassword (
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry,
IN CHAR8 *Password
)
{
EFI_STATUS Status;
HDD_PASSWORD_VARIABLE HddPasswordVariable;
BOOLEAN HashOk;
UINT8 HashData[SHA256_DIGEST_SIZE];
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
if (!GetSavedHddPasswordVariable (ConfigFormEntry, &HddPasswordVariable)) {
DEBUG ((DEBUG_INFO, "GetSavedHddPasswordVariable failed\n"));
return EFI_NOT_FOUND;
}
ZeroMem (HashData, sizeof (HashData));
HashOk = GenerateCredential ((UINT8 *) Password, HDD_PASSWORD_MAX_LENGTH, HddPasswordVariable.PasswordSalt, HashData);
if (!HashOk) {
DEBUG ((DEBUG_INFO, "GenerateCredential failed\n"));
return EFI_DEVICE_ERROR;
}
if (CompareMem (HddPasswordVariable.PasswordHash, HashData, sizeof (HashData)) != 0) {
Status = EFI_INVALID_PARAMETER;
} else {
Status = EFI_SUCCESS;
}
DEBUG ((DEBUG_INFO, "%a() - exit (%r)\n", __FUNCTION__, Status));
return Status;
}
/**
Send unlock hdd password cmd through Ata Pass Thru Protocol.
@param[in] AtaPassThru The pointer to the ATA_PASS_THRU protocol.
@param[in] Port The port number of the ATA device to send the command.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command.
If there is no port multiplier, then specify 0xFFFF.
@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
UnlockHddPassword (
IN EFI_ATA_PASS_THRU_PROTOCOL *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,
NULL
);
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 disable hdd password cmd through Ata Pass Thru Protocol.
@param[in] AtaPassThru The pointer to the ATA_PASS_THRU protocol.
@param[in] Port The port number of the ATA device to send the command.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command.
If there is no port multiplier, then specify 0xFFFF.
@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 disable hdd password cmd.
@retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
@retval EFI_OUT_OF_RESOURCES Not enough memory to disable hdd password cmd.
@retval EFI_DEVICE_ERROR Can not disable hdd password cmd.
**/
EFI_STATUS
DisableHddPassword (
IN EFI_ATA_PASS_THRU_PROTOCOL *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_DIS_PASSWORD_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,
NULL
);
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 set hdd password cmd through Ata Pass Thru Protocol.
@param[in] AtaPassThru The pointer to the ATA_PASS_THRU protocol.
@param[in] Port The port number of the ATA device to send the command.
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command.
If there is no port multiplier, then specify 0xFFFF.
@param[in] Identifier The identifier to set user or master password.
@param[in] SecurityLevel The security level to be set to device.
@param[in] MasterPasswordIdentifier The master password identifier to be set to device.
@param[in] Password The hdd password of attached ATA device.
@retval EFI_SUCCESS Successful to set hdd password cmd.
@retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
@retval EFI_OUT_OF_RESOURCES Not enough memory to set hdd password cmd.
@retval EFI_DEVICE_ERROR Can not set hdd password cmd.
**/
EFI_STATUS
SetHddPassword (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN CHAR8 Identifier,
IN CHAR8 SecurityLevel,
IN CHAR16 MasterPasswordIdentifier,
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_SET_PASSWORD_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 | (UINT16)(SecurityLevel << 8)) & (BIT0 | BIT8);
CopyMem (&((CHAR16 *) Buffer)[1], Password, HDD_PASSWORD_MAX_LENGTH);
if ((Identifier & BIT0) != 0) {
((CHAR16 *) Buffer)[17] = MasterPasswordIdentifier;
}
Packet.OutDataBuffer = Buffer;
Packet.OutTransferLength = sizeof (Buffer);
Packet.Timeout = ATA_TIMEOUT;
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplierPort,
&Packet,
NULL
);
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;
}
/**
Get attached harddisk model number from identify data buffer.
@param[in] IdentifyData Pointer to identify data buffer.
@param[in, out] String The buffer to store harddisk model number.
**/
VOID
GetHddDeviceModelNumber (
IN ATA_IDENTIFY_DATA *IdentifyData,
IN OUT CHAR16 *String
)
{
UINTN Index;
//
// Swap the byte order in the original module name.
// From Ata spec, the maximum length is 40 bytes.
//
for (Index = 0; Index < 40; Index += 2) {
String[Index] = IdentifyData->ModelName[Index + 1];
String[Index + 1] = IdentifyData->ModelName[Index];
}
//
// Chap it off after 20 characters
//
String[20] = L'\0';
return ;
}
/**
Get password input from the popup windows.
@param[in] PopUpString1 Pop up string 1.
@param[in] PopUpString2 Pop up string 2.
@param[in, out] Password The buffer to hold the input password.
@retval EFI_ABORTED It is given up by pressing 'ESC' key.
@retval EFI_SUCCESS Get password input successfully.
**/
EFI_STATUS
PopupHddPasswordInputWindows (
IN CHAR16 *PopUpString1,
IN CHAR16 *PopUpString2,
IN OUT CHAR8 *Password
)
{
EFI_INPUT_KEY Key;
UINTN Length;
CHAR16 Mask[HDD_PASSWORD_MAX_LENGTH + 1];
CHAR16 Unicode[HDD_PASSWORD_MAX_LENGTH + 1];
CHAR8 Ascii[HDD_PASSWORD_MAX_LENGTH + 1];
ZeroMem (Unicode, sizeof (Unicode));
ZeroMem (Ascii, sizeof (Ascii));
ZeroMem (Mask, sizeof (Mask));
gST->ConOut->ClearScreen(gST->ConOut);
Length = 0;
while (TRUE) {
Mask[Length] = L'_';
if (PopUpString2 == NULL) {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
PopUpString1,
L"---------------------",
Mask,
NULL
);
} else {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
PopUpString1,
PopUpString2,
L"---------------------",
Mask,
NULL
);
}
//
// Check key.
//
if (Key.ScanCode == SCAN_NULL) {
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
//
// Add the null terminator.
//
Unicode[Length] = 0;
break;
} else if ((Key.UnicodeChar == CHAR_NULL) ||
(Key.UnicodeChar == CHAR_TAB) ||
(Key.UnicodeChar == CHAR_LINEFEED)
) {
continue;
} else {
if (Key.UnicodeChar == CHAR_BACKSPACE) {
if (Length > 0) {
Unicode[Length] = 0;
Mask[Length] = 0;
Length--;
}
} else {
Unicode[Length] = Key.UnicodeChar;
Mask[Length] = L'*';
Length++;
if (Length == HDD_PASSWORD_MAX_LENGTH) {
//
// Add the null terminator.
//
Unicode[Length] = 0;
Mask[Length] = 0;
break;
}
}
}
}
if (Key.ScanCode == SCAN_ESC) {
ZeroMem (Unicode, sizeof (Unicode));
ZeroMem (Ascii, sizeof (Ascii));
gST->ConOut->ClearScreen(gST->ConOut);
return EFI_ABORTED;
}
}
UnicodeStrToAsciiStrS (Unicode, Ascii, sizeof (Ascii));
CopyMem (Password, Ascii, HDD_PASSWORD_MAX_LENGTH);
ZeroMem (Unicode, sizeof (Unicode));
ZeroMem (Ascii, sizeof (Ascii));
gST->ConOut->ClearScreen(gST->ConOut);
return EFI_SUCCESS;
}
/**
Check if disk is locked, show popup window and ask for password if it is.
@param[in] AtaPassThru Pointer to ATA_PASSTHRU instance.
@param[in] Port The port number of attached ATA device.
@param[in] PortMultiplierPort The port number of port multiplier of attached ATA device.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
**/
VOID
HddPasswordRequestPassword (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry
)
{
EFI_STATUS Status;
CHAR16 PopUpString[100];
ATA_IDENTIFY_DATA IdentifyData;
EFI_INPUT_KEY Key;
UINT16 RetryCount;
CHAR8 Password[HDD_PASSWORD_MAX_LENGTH];
RetryCount = 0;
DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));
UnicodeSPrint (PopUpString, sizeof (PopUpString), L"Unlock: %s", ConfigFormEntry->HddString);
//
// Check the device security status.
//
if ((ConfigFormEntry->IfrData.SecurityStatus.Supported) &&
(ConfigFormEntry->IfrData.SecurityStatus.Enabled)) {
//
// Add PcdSkipHddPasswordPrompt to determin whether to skip password prompt.
// Due to board design, device may not power off during system warm boot, which result in
// security status remain unlocked status, hence we add device security status check here.
//
// If device is in the locked status, device keeps locked and system continues booting.
// If device is in the unlocked status, system is forced shutdown for security concern.
//
if (PcdGetBool (PcdSkipHddPasswordPrompt)) {
if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {
return;
} else {
gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
}
}
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
//
// As soon as the HDD password is in enabled state, we pop up a window to unlock hdd
// no matter it's really in locked or unlocked state.
// This way forces user to enter password every time to provide best safety.
//
while (TRUE) {
Status = PopupHddPasswordInputWindows (PopUpString, NULL, Password);
if (!EFI_ERROR (Status)) {
//
// The HDD is in locked state, unlock it by user input.
//
if (!PasswordIsFullZero (Password)) {
if (!ConfigFormEntry->IfrData.SecurityStatus.Frozen) {
Status = UnlockHddPassword (AtaPassThru, Port, PortMultiplierPort, 0, Password);
} else {
//
// Use saved HDD password variable to validate HDD password
// when the device is at frozen state.
//
Status = ValidateHddPassword (ConfigFormEntry, Password);
}
} else {
Status = EFI_INVALID_PARAMETER;
}
if (!EFI_ERROR (Status)) {
CopyMem (ConfigFormEntry->Password, Password, HDD_PASSWORD_MAX_LENGTH);
if (!ConfigFormEntry->IfrData.SecurityStatus.Frozen) {
SaveHddPasswordVariable (ConfigFormEntry, Password);
}
ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);
Status = GetHddDeviceIdentifyData (AtaPassThru, Port, PortMultiplierPort, &IdentifyData);
ASSERT_EFI_ERROR (Status);
//
// Check the device security status again.
//
GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);
return;
}
ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);
if (EFI_ERROR (Status)) {
RetryCount ++;
if (RetryCount < MAX_HDD_PASSWORD_RETRY_COUNT) {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Invalid password.",
L"Press ENTER to retry",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
continue;
} else {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Hdd password retry count is expired. Please shutdown the machine.",
L"Press ENTER to shutdown",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
break;
}
}
} else if (Status == EFI_ABORTED) {
if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {
//
// Current device in the lock status and
// User not input password and press ESC,
// keep device in lock status and continue boot.
//
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Press ENTER to skip the request and continue boot,",
L"Press ESC to input password again",
NULL
);
} while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
gST->ConOut->ClearScreen(gST->ConOut);
//
// Keep lock and continue boot.
//
return;
} else {
//
// Let user input password again.
//
continue;
}
} else {
//
// Current device in the unlock status and
// User not input password and press ESC,
// Shutdown the device.
//
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Press ENTER to shutdown, Press ESC to input password again",
NULL
);
} while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
} else {
//
// Let user input password again.
//
continue;
}
}
}
}
}
}
/**
Process Set User Pwd HDD password request.
@param[in] AtaPassThru Pointer to ATA_PASSTHRU instance.
@param[in] Port The port number of attached ATA device.
@param[in] PortMultiplierPort The port number of port multiplier of attached ATA device.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
**/
VOID
ProcessHddPasswordRequestSetUserPwd (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry
)
{
EFI_STATUS Status;
CHAR16 PopUpString[100];
ATA_IDENTIFY_DATA IdentifyData;
EFI_INPUT_KEY Key;
UINT16 RetryCount;
CHAR8 Password[HDD_PASSWORD_MAX_LENGTH];
CHAR8 PasswordConfirm[HDD_PASSWORD_MAX_LENGTH];
RetryCount = 0;
DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));
if (ConfigFormEntry->IfrData.SecurityStatus.Frozen) {
DEBUG ((DEBUG_INFO, "%s is frozen, do nothing\n", ConfigFormEntry->HddString));
return;
}
if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {
DEBUG ((DEBUG_INFO, "%s is locked, do nothing\n", ConfigFormEntry->HddString));
return;
}
UnicodeSPrint (PopUpString, sizeof (PopUpString), L"Set User Pwd: %s", ConfigFormEntry->HddString);
//
// Check the device security status.
//
if (ConfigFormEntry->IfrData.SecurityStatus.Supported) {
while (TRUE) {
Status = PopupHddPasswordInputWindows (PopUpString, L"Please type in your new password", Password);
if (!EFI_ERROR (Status)) {
Status = PopupHddPasswordInputWindows (PopUpString, L"Please confirm your new password", PasswordConfirm);
if (!EFI_ERROR (Status)) {
if (CompareMem (Password, PasswordConfirm, HDD_PASSWORD_MAX_LENGTH) == 0) {
if (!PasswordIsFullZero (Password)) {
Status = SetHddPassword (AtaPassThru, Port, PortMultiplierPort, 0, 1, 0, Password);
} else {
if (ConfigFormEntry->IfrData.SecurityStatus.Enabled) {
Status = DisableHddPassword (AtaPassThru, Port, PortMultiplierPort, 0, ConfigFormEntry->Password);
} else {
Status = EFI_INVALID_PARAMETER;
}
}
if (!EFI_ERROR (Status)) {
CopyMem (ConfigFormEntry->Password, Password, HDD_PASSWORD_MAX_LENGTH);
SaveHddPasswordVariable (ConfigFormEntry, Password);
ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);
ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);
Status = GetHddDeviceIdentifyData (AtaPassThru, Port, PortMultiplierPort, &IdentifyData);
ASSERT_EFI_ERROR (Status);
//
// Check the device security status again.
//
GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);
return;
} else {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Set/Disable User Pwd failed or invalid password.",
L"Press ENTER to retry",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}
} else {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Passwords are not the same.",
L"Press ENTER to retry",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
Status = EFI_INVALID_PARAMETER;
}
}
ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);
ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);
if (EFI_ERROR (Status)) {
RetryCount ++;
if (RetryCount >= MAX_HDD_PASSWORD_RETRY_COUNT) {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Hdd password retry count is expired.",
L"Press ENTER to skip the request and continue boot",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
gST->ConOut->ClearScreen(gST->ConOut);
return;
}
}
} else if (Status == EFI_ABORTED) {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Press ENTER to skip the request and continue boot,",
L"Press ESC to input password again",
NULL
);
} while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
gST->ConOut->ClearScreen(gST->ConOut);
return;
} else {
//
// Let user input password again.
//
continue;
}
}
}
}
}
/**
Process Set Master Pwd HDD password request.
@param[in] AtaPassThru Pointer to ATA_PASSTHRU instance.
@param[in] Port The port number of attached ATA device.
@param[in] PortMultiplierPort The port number of port multiplier of attached ATA device.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
**/
VOID
ProcessHddPasswordRequestSetMasterPwd (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry
)
{
EFI_STATUS Status;
CHAR16 PopUpString[100];
EFI_INPUT_KEY Key;
UINT16 RetryCount;
CHAR8 Password[HDD_PASSWORD_MAX_LENGTH];
CHAR8 PasswordConfirm[HDD_PASSWORD_MAX_LENGTH];
RetryCount = 0;
DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));
if (ConfigFormEntry->IfrData.SecurityStatus.Frozen) {
DEBUG ((DEBUG_INFO, "%s is frozen, do nothing\n", ConfigFormEntry->HddString));
return;
}
if (ConfigFormEntry->IfrData.SecurityStatus.Locked) {
DEBUG ((DEBUG_INFO, "%s is locked, do nothing\n", ConfigFormEntry->HddString));
return;
}
UnicodeSPrint (PopUpString, sizeof (PopUpString), L"Set Master Pwd: %s", ConfigFormEntry->HddString);
//
// Check the device security status.
//
if (ConfigFormEntry->IfrData.SecurityStatus.Supported) {
while (TRUE) {
Status = PopupHddPasswordInputWindows (PopUpString, L"Please type in your new password", Password);
if (!EFI_ERROR (Status)) {
Status = PopupHddPasswordInputWindows (PopUpString, L"Please confirm your new password", PasswordConfirm);
if (!EFI_ERROR (Status)) {
if (CompareMem (Password, PasswordConfirm, HDD_PASSWORD_MAX_LENGTH) == 0) {
if (!PasswordIsFullZero (Password)) {
Status = SetHddPassword (AtaPassThru, Port, PortMultiplierPort, 1, 1, 1, Password);
} else {
Status = EFI_INVALID_PARAMETER;
}
if (!EFI_ERROR (Status)) {
ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);
ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);
return;
} else {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Set Master Pwd failed or invalid password.",
L"Press ENTER to retry",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}
} else {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Passwords are not the same.",
L"Press ENTER to retry",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
Status = EFI_INVALID_PARAMETER;
}
}
ZeroMem (Password, HDD_PASSWORD_MAX_LENGTH);
ZeroMem (PasswordConfirm, HDD_PASSWORD_MAX_LENGTH);
if (EFI_ERROR (Status)) {
RetryCount ++;
if (RetryCount >= MAX_HDD_PASSWORD_RETRY_COUNT) {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Hdd password retry count is expired.",
L"Press ENTER to skip the request and continue boot",
NULL
);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
gST->ConOut->ClearScreen(gST->ConOut);
return;
}
}
} else if (Status == EFI_ABORTED) {
do {
CreatePopUp (
EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
&Key,
L"Press ENTER to skip the request and continue boot,",
L"Press ESC to input password again",
NULL
);
} while ((Key.ScanCode != SCAN_ESC) && (Key.UnicodeChar != CHAR_CARRIAGE_RETURN));
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
gST->ConOut->ClearScreen(gST->ConOut);
return;
} else {
//
// Let user input password again.
//
continue;
}
}
}
}
}
/**
Process HDD password request.
@param[in] AtaPassThru Pointer to ATA_PASSTHRU instance.
@param[in] Port The port number of attached ATA device.
@param[in] PortMultiplierPort The port number of port multiplier of attached ATA device.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
**/
VOID
ProcessHddPasswordRequest (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplierPort,
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry
)
{
EFI_STATUS Status;
HDD_PASSWORD_REQUEST_VARIABLE *TempVariable;
HDD_PASSWORD_REQUEST_VARIABLE *Variable;
UINTN VariableSize;
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
if (mHddPasswordRequestVariable == NULL) {
Status = GetVariable2 (
HDD_PASSWORD_REQUEST_VARIABLE_NAME,
&mHddPasswordVendorGuid,
(VOID **) &Variable,
&VariableSize
);
if (EFI_ERROR (Status) || (Variable == NULL)) {
return;
}
mHddPasswordRequestVariable = Variable;
mHddPasswordRequestVariableSize = VariableSize;
//
// Delete the HDD password request variable.
//
Status = gRT->SetVariable (
HDD_PASSWORD_REQUEST_VARIABLE_NAME,
&mHddPasswordVendorGuid,
0,
0,
NULL
);
ASSERT_EFI_ERROR (Status);
} else {
Variable = mHddPasswordRequestVariable;
VariableSize = mHddPasswordRequestVariableSize;
}
//
// Process the HDD password requests.
//
TempVariable = Variable;
while (VariableSize >= sizeof (HDD_PASSWORD_REQUEST_VARIABLE)) {
if ((TempVariable->Device.Bus == ConfigFormEntry->Bus) &&
(TempVariable->Device.Device == ConfigFormEntry->Device) &&
(TempVariable->Device.Function == ConfigFormEntry->Function) &&
(TempVariable->Device.Port == ConfigFormEntry->Port) &&
(TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {
//
// Found the node for the HDD password device.
//
if (TempVariable->Request.UserPassword != 0) {
ProcessHddPasswordRequestSetUserPwd (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);
}
if (TempVariable->Request.MasterPassword != 0) {
ProcessHddPasswordRequestSetMasterPwd (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);
}
break;
}
VariableSize -= sizeof (HDD_PASSWORD_REQUEST_VARIABLE);
TempVariable += 1;
}
DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
}
/**
Get saved HDD password request.
@param[in, out] ConfigFormEntry The HDD Password configuration form entry.
**/
VOID
GetSavedHddPasswordRequest (
IN OUT HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry
)
{
EFI_STATUS Status;
HDD_PASSWORD_REQUEST_VARIABLE *TempVariable;
HDD_PASSWORD_REQUEST_VARIABLE *Variable;
UINTN VariableSize;
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
Variable = NULL;
VariableSize = 0;
Status = GetVariable2 (
HDD_PASSWORD_REQUEST_VARIABLE_NAME,
&mHddPasswordVendorGuid,
(VOID **) &Variable,
&VariableSize
);
if (EFI_ERROR (Status) || (Variable == NULL)) {
return;
}
TempVariable = Variable;
while (VariableSize >= sizeof (HDD_PASSWORD_REQUEST_VARIABLE)) {
if ((TempVariable->Device.Bus == ConfigFormEntry->Bus) &&
(TempVariable->Device.Device == ConfigFormEntry->Device) &&
(TempVariable->Device.Function == ConfigFormEntry->Function) &&
(TempVariable->Device.Port == ConfigFormEntry->Port) &&
(TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {
//
// Found the node for the HDD password device.
// Get the HDD password request.
//
CopyMem (&ConfigFormEntry->IfrData.Request, &TempVariable->Request, sizeof (HDD_PASSWORD_REQUEST));
DEBUG ((
DEBUG_INFO,
"HddPasswordRequest got: 0x%x\n",
ConfigFormEntry->IfrData.Request
));
break;
}
VariableSize -= sizeof (HDD_PASSWORD_REQUEST_VARIABLE);
TempVariable += 1;
}
FreePool (Variable);
DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
}
/**
Save HDD password request.
@param[in] ConfigFormEntry The HDD Password configuration form entry.
**/
VOID
SaveHddPasswordRequest (
IN HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry
)
{
EFI_STATUS Status;
HDD_PASSWORD_REQUEST_VARIABLE *TempVariable;
UINTN TempVariableSize;
HDD_PASSWORD_REQUEST_VARIABLE *Variable;
UINTN VariableSize;
HDD_PASSWORD_REQUEST_VARIABLE *NewVariable;
UINTN NewVariableSize;
DEBUG ((DEBUG_INFO, "%a() - enter\n", __FUNCTION__));
DEBUG ((
DEBUG_INFO,
"HddPasswordRequest to save: 0x%x\n",
ConfigFormEntry->IfrData.Request
));
Variable = NULL;
VariableSize = 0;
NewVariable = NULL;
NewVariableSize = 0;
Status = GetVariable2 (
HDD_PASSWORD_REQUEST_VARIABLE_NAME,
&mHddPasswordVendorGuid,
(VOID **) &Variable,
&VariableSize
);
if (!EFI_ERROR (Status) && (Variable != NULL)) {
TempVariable = Variable;
TempVariableSize = VariableSize;
while (TempVariableSize >= sizeof (HDD_PASSWORD_REQUEST_VARIABLE)) {
if ((TempVariable->Device.Bus == ConfigFormEntry->Bus) &&
(TempVariable->Device.Device == ConfigFormEntry->Device) &&
(TempVariable->Device.Function == ConfigFormEntry->Function) &&
(TempVariable->Device.Port == ConfigFormEntry->Port) &&
(TempVariable->Device.PortMultiplierPort == ConfigFormEntry->PortMultiplierPort)) {
//
// Found the node for the HDD password device.
// Update the HDD password request.
//
CopyMem (&TempVariable->Request, &ConfigFormEntry->IfrData.Request, sizeof (HDD_PASSWORD_REQUEST));
NewVariable = Variable;
NewVariableSize = VariableSize;
break;
}
TempVariableSize -= sizeof (HDD_PASSWORD_REQUEST_VARIABLE);
TempVariable += 1;
}
if (NewVariable == NULL) {
//
// The node for the HDD password device is not found.
// Create node for the HDD password device.
//
NewVariableSize = VariableSize + sizeof (HDD_PASSWORD_REQUEST_VARIABLE);
NewVariable = AllocateZeroPool (NewVariableSize);
ASSERT (NewVariable != NULL);
CopyMem (NewVariable, Variable, VariableSize);
TempVariable = (HDD_PASSWORD_REQUEST_VARIABLE *) ((UINTN) NewVariable + VariableSize);
TempVariable->Device.Bus = (UINT8) ConfigFormEntry->Bus;
TempVariable->Device.Device = (UINT8) ConfigFormEntry->Device;
TempVariable->Device.Function = (UINT8) ConfigFormEntry->Function;
TempVariable->Device.Port = ConfigFormEntry->Port;
TempVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;
CopyMem (&TempVariable->Request, &ConfigFormEntry->IfrData.Request, sizeof (HDD_PASSWORD_REQUEST));
}
} else {
NewVariableSize = sizeof (HDD_PASSWORD_REQUEST_VARIABLE);
NewVariable = AllocateZeroPool (NewVariableSize);
ASSERT (NewVariable != NULL);
NewVariable->Device.Bus = (UINT8) ConfigFormEntry->Bus;
NewVariable->Device.Device = (UINT8) ConfigFormEntry->Device;
NewVariable->Device.Function = (UINT8) ConfigFormEntry->Function;
NewVariable->Device.Port = ConfigFormEntry->Port;
NewVariable->Device.PortMultiplierPort = ConfigFormEntry->PortMultiplierPort;
CopyMem (&NewVariable->Request, &ConfigFormEntry->IfrData.Request, sizeof (HDD_PASSWORD_REQUEST));
}
Status = gRT->SetVariable (
HDD_PASSWORD_REQUEST_VARIABLE_NAME,
&mHddPasswordVendorGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
NewVariableSize,
NewVariable
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "HddPasswordRequest variable set failed (%r)\n", Status));
}
if (NewVariable != Variable) {
FreePool (NewVariable);
}
if (Variable != NULL) {
FreePool (Variable);
}
DEBUG ((DEBUG_INFO, "%a() - exit\n", __FUNCTION__));
}
/**
Get the HDD Password configuration form entry by the index of the goto opcode activated.
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
@param[in] Index The 0-based index of the goto opcode activated.
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
@return The HDD Password configuration form entry found.
**/
HDD_PASSWORD_CONFIG_FORM_ENTRY *
HddPasswordGetConfigFormEntryByIndex (
IN UINT32 Index
)
{
LIST_ENTRY *Entry;
UINT32 CurrentIndex;
HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry;
CurrentIndex = 0;
ConfigFormEntry = NULL;
EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {
if (CurrentIndex == Index) {
ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);
break;
}
CurrentIndex++;
}
return ConfigFormEntry;
}
/**
This function allows the caller to request the current
configuration for one or more named elements. The resulting
string is in <ConfigAltResp> format. Any and all alternative
configuration strings shall also be appended to the end of the
current configuration string. If they are, they must appear
after the current configuration. They must contain the same
routing (GUID, NAME, PATH) as the current configuration string.
They must have an additional description indicating the type of
alternative configuration the string represents,
"ALTCFG=<StringToken>". That <StringToken> (when
converted from Hex UNICODE to binary) is a reference to a
string in the associated string pack.
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param[in] Request A null-terminated Unicode string in
<ConfigRequest> format. Note that this
includes the routing information as well as
the configurable name / value pairs. It is
invalid for this string to be in
<MultiConfigRequest> format.
@param[out] Progress On return, points to a character in the
Request string. Points to the string's null
terminator if request was successful. Points
to the most recent "&" before the first
failing name / value pair (or the beginning
of the string if the failure is in the first
name / value pair) if the request was not
successful.
@param[out] Results A null-terminated Unicode string in
<ConfigAltResp> format which has all values
filled in for the names in the Request string.
String to be allocated by the called function.
@retval EFI_SUCCESS The Results string is filled with the
values corresponding to all requested
names.
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the
parts of the results that must be
stored awaiting possible future
protocols.
@retval EFI_INVALID_PARAMETER For example, passing in a NULL
for the Request parameter
would result in this type of
error. In this case, the
Progress parameter would be
set to NULL.
@retval EFI_NOT_FOUND Routing data doesn't match any
known driver. Progress set to the
first character in the routing header.
Note: There is no requirement that the
driver validate the routing data. It
must skip the <ConfigHdr> in order to
process the names.
@retval EFI_INVALID_PARAMETER Illegal syntax. Progress set
to most recent & before the
error or the beginning of the
string.
@retval EFI_INVALID_PARAMETER Unknown name. Progress points
to the & before the name in
question.Currently not implemented.
**/
EFI_STATUS
EFIAPI
HddPasswordFormExtractConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Request,
OUT EFI_STRING *Progress,
OUT EFI_STRING *Results
)
{
EFI_STATUS Status;
UINTN BufferSize;
HDD_PASSWORD_CONFIG *IfrData;
HDD_PASSWORD_DXE_PRIVATE_DATA *Private;
EFI_STRING ConfigRequestHdr;
EFI_STRING ConfigRequest;
BOOLEAN AllocatedRequest;
UINTN Size;
if (Progress == NULL || Results == NULL) {
return EFI_INVALID_PARAMETER;
}
*Progress = Request;
if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mHddPasswordVendorGuid, mHddPasswordVendorStorageName)) {
return EFI_NOT_FOUND;
}
ConfigRequestHdr = NULL;
ConfigRequest = NULL;
AllocatedRequest = FALSE;
Size = 0;
Private = HDD_PASSWORD_DXE_PRIVATE_FROM_THIS (This);
IfrData = AllocateZeroPool (sizeof (HDD_PASSWORD_CONFIG));
ASSERT (IfrData != NULL);
if (Private->Current != NULL) {
CopyMem (IfrData, &Private->Current->IfrData, sizeof (HDD_PASSWORD_CONFIG));
}
//
// Convert buffer data to <ConfigResp> by helper function BlockToConfig()
//
BufferSize = sizeof (HDD_PASSWORD_CONFIG);
ConfigRequest = Request;
if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
//
// Request has no request element, construct full request string.
// Allocate and fill a buffer large enough to hold the <ConfigHdr> template
// followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
//
ConfigRequestHdr = HiiConstructConfigHdr (&mHddPasswordVendorGuid, mHddPasswordVendorStorageName, Private->DriverHandle);
Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
ConfigRequest = AllocateZeroPool (Size);
ASSERT (ConfigRequest != NULL);
AllocatedRequest = TRUE;
UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
FreePool (ConfigRequestHdr);
}
Status = gHiiConfigRouting->BlockToConfig (
gHiiConfigRouting,
ConfigRequest,
(UINT8 *) IfrData,
BufferSize,
Results,
Progress
);
FreePool (IfrData);
//
// Free the allocated config request string.
//
if (AllocatedRequest) {
FreePool (ConfigRequest);
ConfigRequest = NULL;
}
//
// Set Progress string to the original request string.
//
if (Request == NULL) {
*Progress = NULL;
} else if (StrStr (Request, L"OFFSET") == NULL) {
*Progress = Request + StrLen (Request);
}
return Status;
}
/**
This function applies changes in a driver's configuration.
Input is a Configuration, which has the routing data for this
driver followed by name / value configuration pairs. The driver
must apply those pairs to its configurable storage. If the
driver's configuration is stored in a linear block of data
and the driver's name / value pairs are in <BlockConfig>
format, it may use the ConfigToBlock helper function (above) to
simplify the job. Currently not implemented.
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param[in] Configuration A null-terminated Unicode string in
<ConfigString> format.
@param[out] Progress A pointer to a string filled in with the
offset of the most recent '&' before the
first failing name / value pair (or the
beginn ing of the string if the failure
is in the first name / value pair) or
the terminating NULL if all was
successful.
@retval EFI_SUCCESS The results have been distributed or are
awaiting distribution.
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the
parts of the results that must be
stored awaiting possible future
protocols.
@retval EFI_INVALID_PARAMETERS Passing in a NULL for the
Results parameter would result
in this type of error.
@retval EFI_NOT_FOUND Target for the specified routing data
was not found.
**/
EFI_STATUS
EFIAPI
HddPasswordFormRouteConfig (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN CONST EFI_STRING Configuration,
OUT EFI_STRING *Progress
)
{
if (Configuration == NULL || Progress == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check routing data in <ConfigHdr>.
// Note: if only one Storage is used, then this checking could be skipped.
//
if (!HiiIsConfigHdrMatch (Configuration, &mHddPasswordVendorGuid, mHddPasswordVendorStorageName)) {
*Progress = Configuration;
return EFI_NOT_FOUND;
}
*Progress = Configuration + StrLen (Configuration);
return EFI_SUCCESS;
}
/**
This function is called to provide results data to the driver.
This data consists of a unique key that is used to identify
which data is either being passed back or being asked for.
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
@param[in] Action Specifies the type of action taken by the browser.
@param[in] QuestionId A unique value which is sent to the original
exporting driver so that it can identify the type
of data to expect. The format of the data tends to
vary based on the opcode that enerated the callback.
@param[in] Type The type of value for the question.
@param[in] Value A pointer to the data being sent to the original
exporting driver.
@param[out] ActionRequest On return, points to the action requested by the
callback function.
@retval EFI_SUCCESS The callback successfully handled the action.
@retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
variable and its data.
@retval EFI_DEVICE_ERROR The variable could not be saved.
@retval EFI_UNSUPPORTED The specified Action is not supported by the
callback.Currently not implemented.
@retval EFI_INVALID_PARAMETERS Passing in wrong parameter.
@retval Others Other errors as indicated.
**/
EFI_STATUS
EFIAPI
HddPasswordFormCallback (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN EFI_BROWSER_ACTION Action,
IN EFI_QUESTION_ID QuestionId,
IN UINT8 Type,
IN EFI_IFR_TYPE_VALUE *Value,
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
)
{
HDD_PASSWORD_DXE_PRIVATE_DATA *Private;
EFI_STRING_ID DeviceFormTitleToken;
HDD_PASSWORD_CONFIG *IfrData;
HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry;
if (ActionRequest != NULL) {
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
} else {
return EFI_INVALID_PARAMETER;
}
if ((Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_CHANGED)) {
//
// Do nothing for other UEFI Action. Only do call back when data is changing or changed.
//
return EFI_UNSUPPORTED;
}
Private = HDD_PASSWORD_DXE_PRIVATE_FROM_THIS (This);
//
// Retrive data from Browser
//
IfrData = AllocateZeroPool (sizeof (HDD_PASSWORD_CONFIG));
ASSERT (IfrData != NULL);
if (!HiiGetBrowserData (&mHddPasswordVendorGuid, mHddPasswordVendorStorageName, sizeof (HDD_PASSWORD_CONFIG), (UINT8 *) IfrData)) {
FreePool (IfrData);
return EFI_NOT_FOUND;
}
switch (QuestionId) {
case KEY_HDD_USER_PASSWORD:
if (Action == EFI_BROWSER_ACTION_CHANGED) {
DEBUG ((DEBUG_INFO, "KEY_HDD_USER_PASSWORD\n"));
ConfigFormEntry = Private->Current;
ConfigFormEntry->IfrData.Request.UserPassword = Value->b;
SaveHddPasswordRequest (ConfigFormEntry);
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
}
break;
case KEY_HDD_MASTER_PASSWORD:
if (Action == EFI_BROWSER_ACTION_CHANGED) {
DEBUG ((DEBUG_INFO, "KEY_HDD_MASTER_PASSWORD\n"));
ConfigFormEntry = Private->Current;
ConfigFormEntry->IfrData.Request.MasterPassword = Value->b;
SaveHddPasswordRequest (ConfigFormEntry);
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
}
break;
default:
if ((QuestionId >= KEY_HDD_DEVICE_ENTRY_BASE) && (QuestionId < (mNumberOfHddDevices + KEY_HDD_DEVICE_ENTRY_BASE))) {
if (Action == EFI_BROWSER_ACTION_CHANGING) {
//
// In case goto the device configuration form, update the device form title.
//
ConfigFormEntry = HddPasswordGetConfigFormEntryByIndex ((UINT32) (QuestionId - KEY_HDD_DEVICE_ENTRY_BASE));
ASSERT (ConfigFormEntry != NULL);
DeviceFormTitleToken = (EFI_STRING_ID) STR_HDD_SECURITY_HD;
HiiSetString (Private->HiiHandle, DeviceFormTitleToken, ConfigFormEntry->HddString, NULL);
Private->Current = ConfigFormEntry;
CopyMem (IfrData, &ConfigFormEntry->IfrData, sizeof (HDD_PASSWORD_CONFIG));
}
}
break;
}
//
// Pass changed uncommitted data back to Form Browser
//
HiiSetBrowserData (&mHddPasswordVendorGuid, mHddPasswordVendorStorageName, sizeof (HDD_PASSWORD_CONFIG), (UINT8 *) IfrData, NULL);
FreePool (IfrData);
return EFI_SUCCESS;
}
/**
Updates the HDD Password configuration form to add an entry for the attached
ata harddisk device specified by the Controller.
@param[in] HiiHandle The HII Handle associated with the registered package list.
@param[in] AtaPassThru Pointer to ATA_PASSTHRU instance.
@param[in] PciIo Pointer to PCI_IO instance.
@param[in] Controller The controller handle of the attached ata controller.
@param[in] Bus The bus number of ATA controller.
@param[in] Device The device number of ATA controller.
@param[in] Function The function number of ATA controller.
@param[in] Port The port number of attached ATA device.
@param[in] PortMultiplierPort The port number of port multiplier of attached ATA device.
@retval EFI_SUCCESS The Hdd Password configuration form is updated.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval Others Other errors as indicated.
**/
EFI_STATUS
HddPasswordConfigUpdateForm (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN EFI_HANDLE Controller,
IN UINTN Bus,
IN UINTN Device,
IN UINTN Function,
IN UINT16 Port,
IN UINT16 PortMultiplierPort
)
{
LIST_ENTRY *Entry;
HDD_PASSWORD_CONFIG_FORM_ENTRY *ConfigFormEntry;
BOOLEAN EntryExisted;
EFI_STATUS Status;
VOID *StartOpCodeHandle;
VOID *EndOpCodeHandle;
EFI_IFR_GUID_LABEL *StartLabel;
EFI_IFR_GUID_LABEL *EndLabel;
CHAR16 HddString[40];
ATA_IDENTIFY_DATA IdentifyData;
EFI_DEVICE_PATH_PROTOCOL *AtaDeviceNode;
ConfigFormEntry = NULL;
EntryExisted = FALSE;
EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {
ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);
if ((ConfigFormEntry->Bus == Bus) &&
(ConfigFormEntry->Device == Device) &&
(ConfigFormEntry->Function == Function) &&
(ConfigFormEntry->Port == Port) &&
(ConfigFormEntry->PortMultiplierPort == PortMultiplierPort)) {
EntryExisted = TRUE;
break;
}
}
if (!EntryExisted) {
//
// Add a new form.
//
ConfigFormEntry = AllocateZeroPool (sizeof (HDD_PASSWORD_CONFIG_FORM_ENTRY));
if (ConfigFormEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InitializeListHead (&ConfigFormEntry->Link);
ConfigFormEntry->Controller = Controller;
ConfigFormEntry->Bus = Bus;
ConfigFormEntry->Device = Device;
ConfigFormEntry->Function = Function;
ConfigFormEntry->Port = Port;
ConfigFormEntry->PortMultiplierPort = PortMultiplierPort;
ConfigFormEntry->AtaPassThru = AtaPassThru;
DEBUG ((DEBUG_INFO, "HddPasswordDxe: Create new form for device[%d][%d] at Bus 0x%x Dev 0x%x Func 0x%x\n", Port, PortMultiplierPort, Bus, Device, Function));
//
// Construct the device path for the HDD password device
//
Status = AtaPassThru->BuildDevicePath (
AtaPassThru,
Port,
PortMultiplierPort,
&AtaDeviceNode
);
if (EFI_ERROR (Status)) {
return Status;
}
ConfigFormEntry->DevicePath = AppendDevicePathNode (DevicePathFromHandle (Controller), AtaDeviceNode);
FreePool (AtaDeviceNode);
if (ConfigFormEntry->DevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Get attached harddisk model number
//
Status = GetHddDeviceIdentifyData (AtaPassThru, Port, PortMultiplierPort, &IdentifyData);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
GetHddDeviceModelNumber (&IdentifyData, HddString);
//
// Compose the HDD title string and help string of this port and create a new EFI_STRING_ID.
//
UnicodeSPrint (ConfigFormEntry->HddString, sizeof (ConfigFormEntry->HddString), L"HDD %d:%s", mNumberOfHddDevices, HddString);
ConfigFormEntry->TitleToken = HiiSetString (HiiHandle, 0, ConfigFormEntry->HddString, NULL);
ConfigFormEntry->TitleHelpToken = HiiSetString (HiiHandle, 0, L"Request to set HDD Password", NULL);
GetHddPasswordSecurityStatus (&IdentifyData, &ConfigFormEntry->IfrData);
InsertTailList (&mHddPasswordConfigFormList, &ConfigFormEntry->Link);
//
// Init OpCode Handle
//
StartOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (StartOpCodeHandle != NULL);
EndOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (EndOpCodeHandle != NULL);
//
// Create Hii Extend Label OpCode as the start opcode
//
StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
StartLabel->Number = HDD_DEVICE_ENTRY_LABEL;
//
// Create Hii Extend Label OpCode as the end opcode
//
EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
EndLabel->Number = HDD_DEVICE_LABEL_END;
mNumberOfHddDevices = 0;
EFI_LIST_FOR_EACH (Entry, &mHddPasswordConfigFormList) {
ConfigFormEntry = BASE_CR (Entry, HDD_PASSWORD_CONFIG_FORM_ENTRY, Link);
HiiCreateGotoOpCode (
StartOpCodeHandle, // Container for dynamic created opcodes
FORMID_HDD_DEVICE_FORM, // Target Form ID
ConfigFormEntry->TitleToken, // Prompt text
ConfigFormEntry->TitleHelpToken, // Help text
EFI_IFR_FLAG_CALLBACK, // Question flag
(UINT16) (KEY_HDD_DEVICE_ENTRY_BASE + mNumberOfHddDevices) // Question ID
);
mNumberOfHddDevices++;
}
HiiUpdateForm (
HiiHandle,
&mHddPasswordVendorGuid,
FORMID_HDD_MAIN_FORM,
StartOpCodeHandle,
EndOpCodeHandle
);
HiiFreeOpCodeHandle (StartOpCodeHandle);
HiiFreeOpCodeHandle (EndOpCodeHandle);
//
// Check if device is locked and prompt for password.
//
HddPasswordRequestPassword (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);
//
// Process HDD password request from last boot.
//
ProcessHddPasswordRequest (AtaPassThru, Port, PortMultiplierPort, ConfigFormEntry);
}
return EFI_SUCCESS;
}
/**
Ata Pass Thru Protocol notification event handler.
Check attached harddisk status to see if it's locked. If yes, then pop up a password windows to require user input.
It also registers a form for user configuration on Hdd password configuration.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
HddPasswordNotificationEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
HDD_PASSWORD_DXE_PRIVATE_DATA *Private;
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru;
UINT16 Port;
UINT16 PortMultiplierPort;
EFI_HANDLE Controller;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
EFI_PCI_IO_PROTOCOL *PciIo;
UINTN SegNum;
UINTN BusNum;
UINTN DevNum;
UINTN FuncNum;
if (mHddPasswordEndOfDxe) {
gBS->CloseEvent (Event);
return;
}
Private = (HDD_PASSWORD_DXE_PRIVATE_DATA *)Context;
//
// Locate all handles of AtaPassThru protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiAtaPassThruProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return ;
}
//
// Check attached hard disk status to see if it's locked
//
for (Index = 0; Index < HandleCount; Index += 1) {
Controller = HandleBuffer[Index];
Status = gBS->HandleProtocol (
Controller,
&gEfiAtaPassThruProtocolGuid,
(VOID **) &AtaPassThru
);
if (EFI_ERROR (Status)) {
break;
}
//
// Ignore those logical ATA_PASS_THRU instance.
//
if ((AtaPassThru->Mode->Attributes & EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL) == 0) {
continue;
}
Status = gBS->HandleProtocol (
Controller,
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
break;
}
Status = PciIo->GetLocation (
PciIo,
&SegNum,
&BusNum,
&DevNum,
&FuncNum
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
break;
}
//
// Assume and only support Segment == 0.
//
ASSERT (SegNum == 0);
//
// traverse all attached harddisk devices to update form and unlock it
//
Port = 0xFFFF;
while (TRUE) {
Status = AtaPassThru->GetNextPort (AtaPassThru, &Port);
if (EFI_ERROR (Status)) {
//
// We cannot find more legal port then we are done.
//
break;
}
PortMultiplierPort = 0xFFFF;
while (TRUE) {
Status = AtaPassThru->GetNextDevice (AtaPassThru, 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;
}
//
// Find out the attached harddisk devices.
// Try to add a HDD Password configuration page for the attached devices.
//
gBS->RestoreTPL (TPL_APPLICATION);
Status = HddPasswordConfigUpdateForm (Private->HiiHandle, AtaPassThru, PciIo, Controller, BusNum, DevNum, FuncNum, Port, PortMultiplierPort);
gBS->RaiseTPL (TPL_CALLBACK);
if (EFI_ERROR (Status)) {
break;
}
}
}
}
FreePool (HandleBuffer);
return ;
}
/**
Initialize the HDD Password configuration form.
@param[out] Instance Pointer to private instance.
@retval EFI_SUCCESS The HDD Password configuration form is initialized.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
@retval Others Other errors as indicated.
**/
EFI_STATUS
HddPasswordConfigFormInit (
OUT HDD_PASSWORD_DXE_PRIVATE_DATA **Instance
)
{
EFI_STATUS Status;
HDD_PASSWORD_DXE_PRIVATE_DATA *Private;
InitializeListHead (&mHddPasswordConfigFormList);
Private = AllocateZeroPool (sizeof (HDD_PASSWORD_DXE_PRIVATE_DATA));
if (Private == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Private->Signature = HDD_PASSWORD_DXE_PRIVATE_SIGNATURE;
Private->ConfigAccess.ExtractConfig = HddPasswordFormExtractConfig;
Private->ConfigAccess.RouteConfig = HddPasswordFormRouteConfig;
Private->ConfigAccess.Callback = HddPasswordFormCallback;
//
// Install Device Path Protocol and Config Access protocol to driver handle
//
Status = gBS->InstallMultipleProtocolInterfaces (
&Private->DriverHandle,
&gEfiDevicePathProtocolGuid,
&mHddPasswordHiiVendorDevicePath,
&gEfiHiiConfigAccessProtocolGuid,
&Private->ConfigAccess,
NULL
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
FreePool(Private);
return Status;
}
//
// Publish our HII data
//
Private->HiiHandle = HiiAddPackages (
&mHddPasswordVendorGuid,
Private->DriverHandle,
HddPasswordDxeStrings,
HddPasswordBin,
NULL
);
if (Private->HiiHandle == NULL) {
FreePool(Private);
return EFI_OUT_OF_RESOURCES;
}
*Instance = Private;
return Status;
}
/**
Main entry for this driver.
@param ImageHandle Image handle this driver.
@param SystemTable Pointer to SystemTable.
@retval EFI_SUCCESS This function always complete successfully.
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
**/
EFI_STATUS
EFIAPI
HddPasswordDxeInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
HDD_PASSWORD_DXE_PRIVATE_DATA *Private;
VOID *Registration;
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
EFI_EVENT EndOfDxeEvent;
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
Private = NULL;
//
// Initialize the configuration form of HDD Password.
//
Status = HddPasswordConfigFormInit (&Private);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Register HddPasswordNotificationEvent() notify function.
//
EfiCreateProtocolNotifyEvent (
&gEfiAtaPassThruProtocolGuid,
TPL_CALLBACK,
HddPasswordNotificationEvent,
(VOID *)Private,
&Registration
);
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
HddPasswordEndOfDxeEventNotify,
NULL,
&gEfiEndOfDxeEventGroupGuid,
&EndOfDxeEvent
);
ASSERT_EFI_ERROR (Status);
//
// Make HDD_PASSWORD_VARIABLE_NAME variable read-only.
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
//
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
if (!EFI_ERROR (Status)) {
Status = VariableLock->RequestToLock (
VariableLock,
HDD_PASSWORD_VARIABLE_NAME,
&mHddPasswordVendorGuid
);
DEBUG ((DEBUG_INFO, "%a(): Lock %s variable (%r)\n", __FUNCTION__, HDD_PASSWORD_VARIABLE_NAME, Status));
ASSERT_EFI_ERROR (Status);
}
return Status;
}