diff --git a/SecurityPkg/HddPassword/HddPassword.vfr b/SecurityPkg/HddPassword/HddPassword.vfr new file mode 100644 index 0000000000..2cd39523c7 --- /dev/null +++ b/SecurityPkg/HddPassword/HddPassword.vfr @@ -0,0 +1,188 @@ +/** @file + HDD Password Configuration Formset. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include "HddPasswordHiiDataStruc.h" + +formset + guid = HDD_PASSWORD_CONFIG_GUID, + title = STRING_TOKEN(STR_HDD_SECURITY_CONFIG), + help = STRING_TOKEN(STR_HDD_SECURITY_CONFIG), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + varstore HDD_PASSWORD_CONFIG, + name = HDD_PASSWORD_CONFIG, + guid = HDD_PASSWORD_CONFIG_GUID; + + form formid = FORMID_HDD_MAIN_FORM, + title = STRING_TOKEN(STR_HDD_SECURITY_CONFIG); + + label HDD_DEVICE_ENTRY_LABEL; + label HDD_DEVICE_LABEL_END; + + endform; + + form + formid = FORMID_HDD_DEVICE_FORM, + title = STRING_TOKEN(STR_HDD_SECURITY_HD); + + subtitle text = STRING_TOKEN(STR_SECURITY_HDD_PWD_DESC); + + subtitle text = STRING_TOKEN(STR_NULL); + + subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_ONE); + subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_TWO); + subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_THREE); + subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_FOUR); + subtitle text = STRING_TOKEN(STR_SECURITY_HDD_BANNER_FIVE); + + subtitle text = STRING_TOKEN(STR_NULL); + + subtitle text = STRING_TOKEN(STR_HDD_PASSWORD_CONFIG); + + subtitle text = STRING_TOKEN(STR_NULL); + + grayoutif TRUE; + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 0; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_SUPPORTED), + text = STRING_TOKEN(STR_YES), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 1; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_SUPPORTED), + text = STRING_TOKEN(STR_NO), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Enabled == 0; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_ENABLED), + text = STRING_TOKEN(STR_YES), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Enabled == 1; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_ENABLED), + text = STRING_TOKEN(STR_NO), + flags = 0, + key = 0; + endif; + + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Locked == 0; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_LOCKED), + text = STRING_TOKEN(STR_YES), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Locked == 1; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_LOCKED), + text = STRING_TOKEN(STR_NO), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Frozen == 0; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_FROZEN), + text = STRING_TOKEN(STR_YES), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.Frozen == 1; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_SEC_FROZEN), + text = STRING_TOKEN(STR_NO), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.UserPasswordStatus == 0; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_HDD_USER_PASSWORD_STS), + text = STRING_TOKEN(STR_INSTALLED), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.UserPasswordStatus == 1; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_HDD_USER_PASSWORD_STS), + text = STRING_TOKEN(STR_NOT_INSTALLED), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.MasterPasswordStatus == 0; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_HDD_MASTER_PASSWORD_STS), + text = STRING_TOKEN(STR_INSTALLED), + flags = 0, + key = 0; + endif; + + suppressif ideqvallist HDD_PASSWORD_CONFIG.SecurityStatus.MasterPasswordStatus == 1; + text + help = STRING_TOKEN(STR_EMPTY), + text = STRING_TOKEN(STR_HDD_MASTER_PASSWORD_STS), + text = STRING_TOKEN(STR_NOT_INSTALLED), + flags = 0, + key = 0; + endif; + endif; + + subtitle text = STRING_TOKEN(STR_NULL); + + grayoutif ideqval HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 0; + checkbox varid = HDD_PASSWORD_CONFIG.Request.UserPassword, + prompt = STRING_TOKEN(STR_HDD_USER_PASSWORD), + help = STRING_TOKEN(STR_HDD_USER_PASSWORD_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = KEY_HDD_USER_PASSWORD, + endcheckbox; + endif; + + grayoutif ideqval HDD_PASSWORD_CONFIG.SecurityStatus.Supported == 0; + checkbox varid = HDD_PASSWORD_CONFIG.Request.MasterPassword, + prompt = STRING_TOKEN(STR_HDD_MASTER_PASSWORD), + help = STRING_TOKEN(STR_HDD_MASTER_PASSWORD_HELP), + flags = INTERACTIVE | RESET_REQUIRED, + key = KEY_HDD_MASTER_PASSWORD, + endcheckbox; + endif; + endform; + +endformset; diff --git a/SecurityPkg/HddPassword/HddPasswordCommon.h b/SecurityPkg/HddPassword/HddPasswordCommon.h new file mode 100644 index 0000000000..b904b7d39e --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordCommon.h @@ -0,0 +1,61 @@ +/** @file + HDD Password common header file. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _HDD_PASSWORD_COMMON_H_ +#define _HDD_PASSWORD_COMMON_H_ + +// +// The payload length of HDD related ATA commands +// +#define HDD_PAYLOAD 512 + +#define ATA_SECURITY_SET_PASSWORD_CMD 0xF1 +#define ATA_SECURITY_UNLOCK_CMD 0xF2 +#define ATA_SECURITY_FREEZE_LOCK_CMD 0xF5 +#define ATA_SECURITY_DIS_PASSWORD_CMD 0xF6 + +// +// The max retry count specified in ATA 8 spec. +// +#define MAX_HDD_PASSWORD_RETRY_COUNT 5 + +// +// According to ATA spec, the max length of hdd password is 32 bytes +// +#define HDD_PASSWORD_MAX_LENGTH 32 + +#define HDD_PASSWORD_DEVICE_INFO_GUID { 0x96d877ad, 0x48af, 0x4b39, { 0x9b, 0x27, 0x4d, 0x97, 0x43, 0x9, 0xae, 0x47 } } + +typedef struct { + UINT8 Bus; + UINT8 Device; + UINT8 Function; + UINT8 Reserved; + UINT16 Port; + UINT16 PortMultiplierPort; +} HDD_PASSWORD_DEVICE; + +// +// It will be used to unlock HDD password for S3. +// +typedef struct { + HDD_PASSWORD_DEVICE Device; + CHAR8 Password[HDD_PASSWORD_MAX_LENGTH]; + UINT32 DevicePathLength; + EFI_DEVICE_PATH_PROTOCOL DevicePath[]; +} HDD_PASSWORD_DEVICE_INFO; + +#endif // _HDD_PASSWORD_COMMON_H_ diff --git a/SecurityPkg/HddPassword/HddPasswordDxe.c b/SecurityPkg/HddPassword/HddPasswordDxe.c new file mode 100644 index 0000000000..1247f856db --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordDxe.c @@ -0,0 +1,2814 @@ +/** @file + HDD password driver which is used to support HDD security feature. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "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 comamnd, driver also needs to handle device + // 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)) { + // + // 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 actived. + + @param[in] Index The 0-based index of the goto opcode actived. + + @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 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=". That (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 + 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 + 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 + 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 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 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 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 + 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 + 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 . + // 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_SUCESS This function always complete successfully. + +**/ +EFI_STATUS +EFIAPI +HddPasswordDxeInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + HDD_PASSWORD_DXE_PRIVATE_DATA *Private; + EFI_EVENT Registration; + 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 varible read-only. + // + 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; +} diff --git a/SecurityPkg/HddPassword/HddPasswordDxe.h b/SecurityPkg/HddPassword/HddPasswordDxe.h new file mode 100644 index 0000000000..41db0554d5 --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordDxe.h @@ -0,0 +1,148 @@ +/** @file + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _HDD_PASSWORD_DXE_H_ +#define _HDD_PASSWORD_DXE_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HddPasswordCommon.h" +#include "HddPasswordHiiDataStruc.h" + +// +// This is the generated IFR binary data for each formset defined in VFR. +// This data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 HddPasswordBin[]; + +// +// This is the generated String package data for all .UNI files. +// This data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 HddPasswordDxeStrings[]; + +#define HDD_PASSWORD_DXE_PRIVATE_SIGNATURE SIGNATURE_32 ('H', 'D', 'D', 'P') + +typedef struct _HDD_PASSWORD_CONFIG_FORM_ENTRY { + LIST_ENTRY Link; + EFI_HANDLE Controller; + UINTN Bus; + UINTN Device; + UINTN Function; + UINT16 Port; + UINT16 PortMultiplierPort; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + CHAR16 HddString[64]; + CHAR8 Password[HDD_PASSWORD_MAX_LENGTH]; + EFI_STRING_ID TitleToken; + EFI_STRING_ID TitleHelpToken; + + HDD_PASSWORD_CONFIG IfrData; + EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru; +} HDD_PASSWORD_CONFIG_FORM_ENTRY; + +typedef struct _HDD_PASSWORD_DXE_PRIVATE_DATA { + UINTN Signature; + EFI_HANDLE DriverHandle; + EFI_HII_HANDLE HiiHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; + HDD_PASSWORD_CONFIG_FORM_ENTRY *Current; +} HDD_PASSWORD_DXE_PRIVATE_DATA; + +#define HDD_PASSWORD_DXE_PRIVATE_FROM_THIS(a) CR (a, HDD_PASSWORD_DXE_PRIVATE_DATA, ConfigAccess, HDD_PASSWORD_DXE_PRIVATE_SIGNATURE) + +// +//Iterate through the doule linked list. NOT delete safe +// +#define EFI_LIST_FOR_EACH(Entry, ListHead) \ + for (Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink) + +#define PASSWORD_SALT_SIZE 32 + +#define HDD_PASSWORD_REQUEST_VARIABLE_NAME L"HddPasswordRequest" + +// +// It needs to be locked before EndOfDxe. +// +#define HDD_PASSWORD_VARIABLE_NAME L"HddPassword" + +#pragma pack(1) + +typedef struct { + HDD_PASSWORD_DEVICE Device; + HDD_PASSWORD_REQUEST Request; +} HDD_PASSWORD_REQUEST_VARIABLE; + +// +// It will be used to validate HDD password when the device is at frozen state. +// +typedef struct { + HDD_PASSWORD_DEVICE Device; + UINT8 PasswordHash[SHA256_DIGEST_SIZE]; + UINT8 PasswordSalt[PASSWORD_SALT_SIZE]; +} HDD_PASSWORD_VARIABLE; + +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +#pragma pack() + +// +// Time out value for ATA pass through protocol +// +#define ATA_TIMEOUT EFI_TIMER_PERIOD_SECONDS (3) + +typedef struct { + UINT32 Address; + S3_BOOT_SCRIPT_LIB_WIDTH Width; +} HDD_HC_PCI_REGISTER_SAVE; + +#endif diff --git a/SecurityPkg/HddPassword/HddPasswordDxe.inf b/SecurityPkg/HddPassword/HddPasswordDxe.inf new file mode 100644 index 0000000000..7a3fc2f88c --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordDxe.inf @@ -0,0 +1,75 @@ +## @file +# HddPasswordDxe driver which is used to set/clear hdd password at attached harddisk +# devices. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HddPasswordDxe + FILE_GUID = 9BD549CD-86D1-4925-9F7D-3686DDD876FC + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HddPasswordDxeInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + HddPasswordDxe.c + HddPasswordDxe.h + HddPasswordHiiDataStruc.h + HddPassword.vfr + HddPasswordStrings.uni + HddPasswordCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + +[LibraryClasses] + BaseLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiHiiServicesLib + UefiRuntimeServicesTableLib + DxeServicesTableLib + BaseMemoryLib + DebugLib + HiiLib + PrintLib + UefiLib + LockBoxLib + S3BootScriptLib + PciLib + BaseCryptLib + +[Guids] + gEfiIfrTianoGuid ## CONSUMES ## GUID + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + gS3StorageDeviceInitListGuid ## SOMETIMES_PRODUCES ## UNDEFINED + +[Protocols] + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + gEfiAtaPassThruProtocolGuid ## CONSUMES + gEfiPciIoProtocolGuid ## CONSUMES + gEdkiiVariableLockProtocolGuid ## CONSUMES + +[Depex] + gEfiVariableWriteArchProtocolGuid + diff --git a/SecurityPkg/HddPassword/HddPasswordHiiDataStruc.h b/SecurityPkg/HddPassword/HddPasswordHiiDataStruc.h new file mode 100644 index 0000000000..608b92d797 --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordHiiDataStruc.h @@ -0,0 +1,63 @@ +/** @file + HddPassword HII data structure used by the driver. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _HDD_PASSWORD_HII_DATASTRUC_H_ +#define _HDD_PASSWORD_HII_DATASTRUC_H_ + +#include + +#define HDD_PASSWORD_CONFIG_GUID \ + { \ + 0x737cded7, 0x448b, 0x4801, { 0xb5, 0x7d, 0xb1, 0x94, 0x83, 0xec, 0x60, 0x6f } \ + } + +#define FORMID_HDD_MAIN_FORM 1 +#define FORMID_HDD_DEVICE_FORM 2 + +#define HDD_DEVICE_ENTRY_LABEL 0x1234 +#define HDD_DEVICE_LABEL_END 0xffff + +#define KEY_HDD_DEVICE_ENTRY_BASE 0x1000 + +#define KEY_HDD_USER_PASSWORD 0x101 +#define KEY_HDD_MASTER_PASSWORD 0x102 + +#pragma pack(1) + +typedef struct { + UINT8 Supported:1; + UINT8 Enabled:1; + UINT8 Locked:1; + UINT8 Frozen:1; + UINT8 UserPasswordStatus:1; + UINT8 MasterPasswordStatus:1; + UINT8 Reserved:2; +} HDD_PASSWORD_SECURITY_STATUS; + +typedef struct { + UINT8 UserPassword:1; + UINT8 MasterPassword:1; + UINT8 Reserved:6; +} HDD_PASSWORD_REQUEST; + +typedef struct _HDD_PASSWORD_CONFIG { + HDD_PASSWORD_SECURITY_STATUS SecurityStatus; + HDD_PASSWORD_REQUEST Request; +} HDD_PASSWORD_CONFIG; + +#pragma pack() + +#endif diff --git a/SecurityPkg/HddPassword/HddPasswordPei.c b/SecurityPkg/HddPassword/HddPasswordPei.c new file mode 100644 index 0000000000..1ea63b84bb --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordPei.c @@ -0,0 +1,374 @@ +/** @file + HddPassword PEI module which is used to unlock HDD password for S3. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HddPasswordPei.h" + +EFI_GUID mHddPasswordDeviceInfoGuid = HDD_PASSWORD_DEVICE_INFO_GUID; + + +/** + Send unlock hdd password cmd through ATA PassThru PPI. + + @param[in] AtaPassThru The pointer to the ATA PassThru PPI. + @param[in] Port The port number of the ATA device. + @param[in] PortMultiplierPort The port multiplier port number of the ATA device. + @param[in] Identifier The identifier to set user or master password. + @param[in] Password The hdd password of attached ATA device. + + @retval EFI_SUCCESS Successful to send unlock hdd password cmd. + @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid. + @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd. + @retval EFI_DEVICE_ERROR Can not send unlock hdd password cmd. + +**/ +EFI_STATUS +UnlockDevice ( + IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru, + IN UINT16 Port, + IN UINT16 PortMultiplierPort, + IN CHAR8 Identifier, + IN CHAR8 *Password + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK Acb; + EFI_ATA_STATUS_BLOCK *Asb; + EFI_ATA_PASS_THRU_COMMAND_PACKET Packet; + UINT8 Buffer[HDD_PAYLOAD]; + + if ((AtaPassThru == NULL) || (Password == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in + // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by + // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile, + // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it + // may not be aligned when allocated on stack for some compilers. Hence, we + // use the API AllocateAlignedPages to ensure this structure is properly + // aligned. + // + Asb = AllocateAlignedPages ( + EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)), + AtaPassThru->Mode->IoAlign + ); + if (Asb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Prepare for ATA command block. + // + ZeroMem (&Acb, sizeof (Acb)); + ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK)); + Acb.AtaCommand = ATA_SECURITY_UNLOCK_CMD; + Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4)); + + // + // Prepare for ATA pass through packet. + // + ZeroMem (&Packet, sizeof (Packet)); + Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT; + Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES; + Packet.Asb = Asb; + Packet.Acb = &Acb; + + ((CHAR16 *) Buffer)[0] = Identifier & BIT0; + CopyMem (&((CHAR16 *) Buffer)[1], Password, HDD_PASSWORD_MAX_LENGTH); + + Packet.OutDataBuffer = Buffer; + Packet.OutTransferLength = sizeof (Buffer); + Packet.Timeout = ATA_TIMEOUT; + + Status = AtaPassThru->PassThru ( + AtaPassThru, + Port, + PortMultiplierPort, + &Packet + ); + if (!EFI_ERROR (Status) && + ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) && + ((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) { + Status = EFI_DEVICE_ERROR; + } + + FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK))); + + ZeroMem (Buffer, sizeof (Buffer)); + + DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status)); + return Status; +} + +/** + Send security freeze lock cmd through ATA PassThru PPI. + + @param[in] AtaPassThru The pointer to the ATA PassThru PPI. + @param[in] Port The port number of the ATA device. + @param[in] PortMultiplierPort The port multiplier port number of the ATA device. + + @retval EFI_SUCCESS Successful to send security freeze lock cmd. + @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid. + @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd. + @retval EFI_DEVICE_ERROR Can not send security freeze lock cmd. + +**/ +EFI_STATUS +FreezeLockDevice ( + IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru, + IN UINT16 Port, + IN UINT16 PortMultiplierPort + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK Acb; + EFI_ATA_STATUS_BLOCK *Asb; + EFI_ATA_PASS_THRU_COMMAND_PACKET Packet; + + if (AtaPassThru == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in + // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by + // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile, + // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it + // may not be aligned when allocated on stack for some compilers. Hence, we + // use the API AllocateAlignedPages to ensure this structure is properly + // aligned. + // + Asb = AllocateAlignedPages ( + EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)), + AtaPassThru->Mode->IoAlign + ); + if (Asb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Prepare for ATA command block. + // + ZeroMem (&Acb, sizeof (Acb)); + ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK)); + Acb.AtaCommand = ATA_SECURITY_FREEZE_LOCK_CMD; + Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4)); + + // + // Prepare for ATA pass through packet. + // + ZeroMem (&Packet, sizeof (Packet)); + Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA; + Packet.Length = EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER; + Packet.Asb = Asb; + Packet.Acb = &Acb; + Packet.Timeout = ATA_TIMEOUT; + + Status = AtaPassThru->PassThru ( + AtaPassThru, + Port, + PortMultiplierPort, + &Packet + ); + if (!EFI_ERROR (Status) && + ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) && + ((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) { + Status = EFI_DEVICE_ERROR; + } + + FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK))); + + DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status)); + return Status; +} + +/** + Unlock HDD password for S3. + + @param[in] AtaPassThruPpi Pointer to the EDKII_PEI_ATA_PASS_THRU_PPI instance. + +**/ +VOID +UnlockHddPassword ( + IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThruPpi + ) +{ + EFI_STATUS Status; + VOID *Buffer; + UINTN Length; + UINT8 DummyData; + HDD_PASSWORD_DEVICE_INFO *DevInfo; + UINT16 Port; + UINT16 PortMultiplierPort; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN DevicePathLength; + + // + // Get HDD password device info from LockBox. + // + Buffer = (VOID *) &DummyData; + Length = sizeof (DummyData); + Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length); + if (Status == EFI_BUFFER_TOO_SMALL) { + Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Length)); + if (Buffer != NULL) { + Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length); + } + } + if ((Buffer == NULL) || (Buffer == (VOID *) &DummyData)) { + return; + } else if (EFI_ERROR (Status)) { + FreePages (Buffer, EFI_SIZE_TO_PAGES (Length)); + return; + } + + Status = AtaPassThruPpi->GetDevicePath (AtaPassThruPpi, &DevicePathLength, &DevicePath); + if (EFI_ERROR (Status) || (DevicePathLength <= sizeof (EFI_DEVICE_PATH_PROTOCOL))) { + goto Exit; + } + + // + // Go through all the devices managed by the AtaPassThru PPI instance. + // + Port = 0xFFFF; + while (TRUE) { + Status = AtaPassThruPpi->GetNextPort (AtaPassThruPpi, &Port); + if (EFI_ERROR (Status)) { + // + // We cannot find more legal port then we are done. + // + break; + } + + PortMultiplierPort = 0xFFFF; + while (TRUE) { + Status = AtaPassThruPpi->GetNextDevice (AtaPassThruPpi, Port, &PortMultiplierPort); + if (EFI_ERROR (Status)) { + // + // We cannot find more legal port multiplier port number for ATA device + // on the port, then we are done. + // + break; + } + + // + // Search the device in the restored LockBox. + // + DevInfo = (HDD_PASSWORD_DEVICE_INFO *) Buffer; + while ((UINTN) DevInfo < ((UINTN) Buffer + Length)) { + // + // Find the matching device. + // + if ((DevInfo->Device.Port == Port) && + (DevInfo->Device.PortMultiplierPort == PortMultiplierPort) && + (DevInfo->DevicePathLength >= DevicePathLength) && + (CompareMem ( + DevInfo->DevicePath, + DevicePath, + DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) == 0)) { + // + // If device locked, unlock first. + // + if (!IsZeroBuffer (DevInfo->Password, HDD_PASSWORD_MAX_LENGTH)) { + UnlockDevice (AtaPassThruPpi, Port, PortMultiplierPort, 0, DevInfo->Password); + } + // + // Freeze lock the device. + // + FreezeLockDevice (AtaPassThruPpi, Port, PortMultiplierPort); + break; + } + + DevInfo = (HDD_PASSWORD_DEVICE_INFO *) + ((UINTN) DevInfo + sizeof (HDD_PASSWORD_DEVICE_INFO) + DevInfo->DevicePathLength); + } + } + } + +Exit: + ZeroMem (Buffer, Length); + FreePages (Buffer, EFI_SIZE_TO_PAGES (Length)); + +} + +/** + Entry point of the notification callback function itself within the PEIM. + It is to unlock HDD password for S3. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @return Status of the notification. + The status code returned from this function is ignored. +**/ +EFI_STATUS +EFIAPI +HddPasswordAtaPassThruNotify ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, + IN VOID *Ppi + ) +{ + DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__)); + + UnlockHddPassword ((EDKII_PEI_ATA_PASS_THRU_PPI *) Ppi); + + DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__)); + + return EFI_SUCCESS; +} + + +EFI_PEI_NOTIFY_DESCRIPTOR mHddPasswordAtaPassThruPpiNotifyDesc = { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiPeiAtaPassThruPpiGuid, + HddPasswordAtaPassThruNotify +}; + + +/** + Main entry for this module. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Pointer to PEI Services table. + + @return Status from PeiServicesNotifyPpi. + +**/ +EFI_STATUS +EFIAPI +HddPasswordPeiInit ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + + Status = PeiServicesGetBootMode (&BootMode); + if ((EFI_ERROR (Status)) || (BootMode != BOOT_ON_S3_RESUME)) { + return EFI_UNSUPPORTED; + } + + DEBUG ((DEBUG_INFO, "%a: Enters in S3 path.\n", __FUNCTION__)); + + Status = PeiServicesNotifyPpi (&mHddPasswordAtaPassThruPpiNotifyDesc); + ASSERT_EFI_ERROR (Status); + return Status; +} + diff --git a/SecurityPkg/HddPassword/HddPasswordPei.h b/SecurityPkg/HddPassword/HddPasswordPei.h new file mode 100644 index 0000000000..813b5422b4 --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordPei.h @@ -0,0 +1,42 @@ +/** @file + HddPassword PEI module which is used to unlock HDD password for S3. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions + of the BSD License which accompanies this distribution. The + full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _HDD_PASSWORD_PEI_H_ +#define _HDD_PASSWORD_PEI_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "HddPasswordCommon.h" + + +// +// Time out value for ATA PassThru PPI +// +#define ATA_TIMEOUT 30000000 + +#endif diff --git a/SecurityPkg/HddPassword/HddPasswordPei.inf b/SecurityPkg/HddPassword/HddPasswordPei.inf new file mode 100644 index 0000000000..d240cc1d07 --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordPei.inf @@ -0,0 +1,54 @@ +## @file +# HddPassword PEI module which is used to unlock HDD password for S3. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HddPasswordPei + FILE_GUID = 91AD7375-8E8E-49D2-A343-68BC78273955 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = HddPasswordPeiInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + HddPasswordPei.c + HddPasswordPei.h + HddPasswordCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesLib + DebugLib + BaseLib + BaseMemoryLib + MemoryAllocationLib + PciLib + LockBoxLib + +[Ppis] + gEdkiiPeiAtaPassThruPpiGuid ## NOTIFY + +[Depex] + gEfiPeiMasterBootModePpiGuid + diff --git a/SecurityPkg/HddPassword/HddPasswordStrings.uni b/SecurityPkg/HddPassword/HddPasswordStrings.uni new file mode 100644 index 0000000000..455ecfcd02 --- /dev/null +++ b/SecurityPkg/HddPassword/HddPasswordStrings.uni @@ -0,0 +1,48 @@ +// /** @file +// String definitions for HddPassword Setup Form. +// +// Copyright (c) 2019, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions +// of the BSD License which accompanies this distribution. The +// full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#langdef en-US "English" + +#string STR_HDD_SECURITY_CONFIG #language en-US "HDD Security Configuration" + +#string STR_SECURITY_HDD_PWD_DESC #language en-US "HDD Password Description :" + +#string STR_SECURITY_HDD_BANNER_ONE #language en-US "Allows Access to Set, Modify and Clear HardDisk User and" +#string STR_SECURITY_HDD_BANNER_TWO #language en-US "Master Passwords." +#string STR_SECURITY_HDD_BANNER_THREE #language en-US "User Password need to be installed for Enabling Security." +#string STR_SECURITY_HDD_BANNER_FOUR #language en-US "Master Password can be modified only when succesfully" +#string STR_SECURITY_HDD_BANNER_FIVE #language en-US "unlocked with User Password in POST." + +#string STR_HDD_SECURITY_HD #language en-US "HDD Password" +#string STR_HDD_SECURITY_HELP #language en-US "Set HDD Password" +#string STR_HDD_PASSWORD_CONFIG #language en-US "HDD PASSWORD CONFIGURATION:" +#string STR_SEC_SUPPORTED #language en-US "Security Supported :" +#string STR_SEC_ENABLED #language en-US "Security Enabled :" +#string STR_SEC_LOCKED #language en-US "Security Locked :" +#string STR_SEC_FROZEN #language en-US "Security Frozen :" +#string STR_YES #language en-US "Yes" +#string STR_NO #language en-US "No" +#string STR_HDD_USER_PASSWORD #language en-US "Request to set User Password" +#string STR_HDD_USER_PASSWORD_HELP #language en-US "Request to set HDD User Password. \n*** Reset is required for the request to be processed in next boot *** \n*** G3 circle is required to disable freeze state when Security Frozen state is Yes, otherwise the request will be ignored. *** " +#string STR_HDD_MASTER_PASSWORD #language en-US "Request to set Master Password" +#string STR_HDD_MASTER_PASSWORD_HELP #language en-US "Request to set HDD Master Password. \n*** Reset is required for the request to be processed in next boot *** \n*** G3 circle is required to disable freeze state when Security Frozen state is Yes, otherwise the request will be ignored. *** " + +#string STR_INSTALLED #language en-US "INSTALLED" +#string STR_NOT_INSTALLED #language en-US "NOT INSTALLED" +#string STR_HDD_USER_PASSWORD_STS #language en-US "HDD User Password Status :" +#string STR_HDD_MASTER_PASSWORD_STS #language en-US "HDD Master Password Status:" +#string STR_NULL #language en-US "" +#string STR_EMPTY #language en-US "" diff --git a/SecurityPkg/SecurityPkg.dsc b/SecurityPkg/SecurityPkg.dsc index ab887e8c4d..5577ff0687 100644 --- a/SecurityPkg/SecurityPkg.dsc +++ b/SecurityPkg/SecurityPkg.dsc @@ -287,6 +287,12 @@ SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordDxe.inf SecurityPkg/Tcg/Opal/OpalPassword/OpalPasswordPei.inf + # + # HDD Password solution + # + SecurityPkg/HddPassword/HddPasswordDxe.inf + SecurityPkg/HddPassword/HddPasswordPei.inf + [BuildOptions] MSFT:*_*_IA32_DLINK_FLAGS = /ALIGN:256 INTEL:*_*_IA32_DLINK_FLAGS = /ALIGN:256